From 82c12cc8ae1564b05c2e4d71ebee3fad9b012bf0 Mon Sep 17 00:00:00 2001 From: Bethuel Mmbaga Date: Tue, 25 Feb 2025 19:57:04 +0000 Subject: [PATCH 01/30] [management] Handle transaction error on peer deletion (#3387) Signed-off-by: bcmmbaga --- management/server/peer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/management/server/peer.go b/management/server/peer.go index c9b0fcfee..ff898f56c 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -400,6 +400,9 @@ func (am *DefaultAccountManager) DeletePeer(ctx context.Context, accountID, peer eventsToStore, err = deletePeers(ctx, am, transaction, accountID, userID, []*nbpeer.Peer{peer}) return err }) + if err != nil { + return err + } for _, storeEvent := range eventsToStore { storeEvent() From 9d604b7e662cd837a1e3a1dc2e67135884550c74 Mon Sep 17 00:00:00 2001 From: Philippe Vaucher Date: Mon, 3 Mar 2025 17:22:51 +0100 Subject: [PATCH 02/30] [client Fix env var typo (#3415) --- client/iface/netstack/tun.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/iface/netstack/tun.go b/client/iface/netstack/tun.go index 01f19875e..a271a1954 100644 --- a/client/iface/netstack/tun.go +++ b/client/iface/netstack/tun.go @@ -55,7 +55,7 @@ func (t *NetStackTun) Create() (tun.Device, *netstack.Net, error) { skipProxy, err := strconv.ParseBool(os.Getenv(EnvSkipProxy)) if err != nil { - log.Errorf("failed to parse NB_ETSTACK_SKIP_PROXY: %s", err) + log.Errorf("failed to parse %s: %s", EnvSkipProxy, err) } if skipProxy { return nsTunDev, tunNet, nil From af5796de1c25136f23b7289de78b5680e985a4dc Mon Sep 17 00:00:00 2001 From: robertgro <34941707+robertgro@users.noreply.github.com> Date: Mon, 3 Mar 2025 17:32:50 +0100 Subject: [PATCH 03/30] [client] Add Netbird GitHub link to the client ui about sub menu (#3372) --- client/ui/client_ui.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/ui/client_ui.go b/client/ui/client_ui.go index 51eec59a5..889db70d6 100644 --- a/client/ui/client_ui.go +++ b/client/ui/client_ui.go @@ -154,6 +154,7 @@ type serviceClient struct { mAdminPanel *systray.MenuItem mSettings *systray.MenuItem mAbout *systray.MenuItem + mGitHub *systray.MenuItem mVersionUI *systray.MenuItem mVersionDaemon *systray.MenuItem mUpdate *systray.MenuItem @@ -607,6 +608,9 @@ func (s *serviceClient) onTrayReady() { s.mAbout = systray.AddMenuItem("About", "About") s.mAbout.SetIcon(s.icAbout) + + s.mGitHub = s.mAbout.AddSubMenuItem("GitHub", "GitHub") + versionString := normalizedVersion(version.NetbirdVersion()) s.mVersionUI = s.mAbout.AddSubMenuItem(fmt.Sprintf("GUI: %s", versionString), fmt.Sprintf("GUI Version: %s", versionString)) s.mVersionUI.Disable() @@ -717,6 +721,11 @@ func (s *serviceClient) onTrayReady() { case <-s.mQuit.ClickedCh: systray.Quit() return + case <-s.mGitHub.ClickedCh: + err := openURL("https://github.com/netbirdio/netbird") + if err != nil { + log.Errorf("%s", err) + } case <-s.mUpdate.ClickedCh: err := openURL(version.DownloadUrl()) if err != nil { From bcc5824980c4d510e8bd777325f602dce51518e6 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:19:42 +0100 Subject: [PATCH 04/30] [client] Close userspace firewall properly (#3426) --- client/firewall/iptables/manager_linux.go | 2 +- client/firewall/iptables/manager_linux_test.go | 12 ++++++------ client/firewall/iptables/state_linux.go | 2 +- client/firewall/manager/firewall.go | 4 ++-- client/firewall/nftables/manager_linux.go | 4 ++-- client/firewall/nftables/manager_linux_test.go | 8 ++++---- client/firewall/nftables/router_linux_test.go | 4 ++-- client/firewall/nftables/state_linux.go | 2 +- client/firewall/uspfilter/allow_netbird.go | 8 ++------ .../firewall/uspfilter/allow_netbird_windows.go | 8 ++------ .../firewall/uspfilter/uspfilter_bench_test.go | 16 ++++++++-------- .../firewall/uspfilter/uspfilter_filter_test.go | 4 ++-- client/firewall/uspfilter/uspfilter_test.go | 12 ++++++------ client/internal/acl/manager_test.go | 4 ++-- client/internal/dns/server_test.go | 2 +- client/internal/engine.go | 2 +- 16 files changed, 43 insertions(+), 51 deletions(-) diff --git a/client/firewall/iptables/manager_linux.go b/client/firewall/iptables/manager_linux.go index 929e8a656..144d5a17f 100644 --- a/client/firewall/iptables/manager_linux.go +++ b/client/firewall/iptables/manager_linux.go @@ -166,7 +166,7 @@ func (m *Manager) SetLegacyManagement(isLegacy bool) error { } // Reset firewall to the default state -func (m *Manager) Reset(stateManager *statemanager.Manager) error { +func (m *Manager) Close(stateManager *statemanager.Manager) error { m.mutex.Lock() defer m.mutex.Unlock() diff --git a/client/firewall/iptables/manager_linux_test.go b/client/firewall/iptables/manager_linux_test.go index ba578c033..856633409 100644 --- a/client/firewall/iptables/manager_linux_test.go +++ b/client/firewall/iptables/manager_linux_test.go @@ -62,7 +62,7 @@ func TestIptablesManager(t *testing.T) { time.Sleep(time.Second) defer func() { - err := manager.Reset(nil) + err := manager.Close(nil) require.NoError(t, err, "clear the manager state") time.Sleep(time.Second) @@ -100,14 +100,14 @@ func TestIptablesManager(t *testing.T) { _, err = manager.AddPeerFiltering(ip, "udp", nil, port, fw.ActionAccept, "", "accept Fake DNS traffic") require.NoError(t, err, "failed to add rule") - err = manager.Reset(nil) + err = manager.Close(nil) require.NoError(t, err, "failed to reset") ok, err := ipv4Client.ChainExists("filter", chainNameInputRules) require.NoError(t, err, "failed check chain exists") if ok { - require.NoErrorf(t, err, "chain '%v' still exists after Reset", chainNameInputRules) + require.NoErrorf(t, err, "chain '%v' still exists after Close", chainNameInputRules) } }) } @@ -136,7 +136,7 @@ func TestIptablesManagerIPSet(t *testing.T) { time.Sleep(time.Second) defer func() { - err := manager.Reset(nil) + err := manager.Close(nil) require.NoError(t, err, "clear the manager state") time.Sleep(time.Second) @@ -166,7 +166,7 @@ func TestIptablesManagerIPSet(t *testing.T) { }) t.Run("reset check", func(t *testing.T) { - err = manager.Reset(nil) + err = manager.Close(nil) require.NoError(t, err, "failed to reset") }) } @@ -204,7 +204,7 @@ func TestIptablesCreatePerformance(t *testing.T) { time.Sleep(time.Second) defer func() { - err := manager.Reset(nil) + err := manager.Close(nil) require.NoError(t, err, "clear the manager state") time.Sleep(time.Second) diff --git a/client/firewall/iptables/state_linux.go b/client/firewall/iptables/state_linux.go index 44b8340ba..2a7120bbf 100644 --- a/client/firewall/iptables/state_linux.go +++ b/client/firewall/iptables/state_linux.go @@ -62,7 +62,7 @@ func (s *ShutdownState) Cleanup() error { ipt.aclMgr.ipsetStore = s.ACLIPsetStore } - if err := ipt.Reset(nil); err != nil { + if err := ipt.Close(nil); err != nil { return fmt.Errorf("reset iptables manager: %w", err) } diff --git a/client/firewall/manager/firewall.go b/client/firewall/manager/firewall.go index d007e20a5..e71328a44 100644 --- a/client/firewall/manager/firewall.go +++ b/client/firewall/manager/firewall.go @@ -94,8 +94,8 @@ type Manager interface { // SetLegacyManagement sets the legacy management mode SetLegacyManagement(legacy bool) error - // Reset firewall to the default state - Reset(stateManager *statemanager.Manager) error + // Close closes the firewall manager + Close(stateManager *statemanager.Manager) error // Flush the changes to firewall controller Flush() error diff --git a/client/firewall/nftables/manager_linux.go b/client/firewall/nftables/manager_linux.go index de68f3291..3df9b378d 100644 --- a/client/firewall/nftables/manager_linux.go +++ b/client/firewall/nftables/manager_linux.go @@ -87,7 +87,7 @@ func (m *Manager) Init(stateManager *statemanager.Manager) error { // We only need to record minimal interface state for potential recreation. // Unlike iptables, which requires tracking individual rules, nftables maintains // a known state (our netbird table plus a few static rules). This allows for easy - // cleanup using Reset() without needing to store specific rules. + // cleanup using Close() without needing to store specific rules. if err := stateManager.UpdateState(&ShutdownState{ InterfaceState: &InterfaceState{ NameStr: m.wgIface.Name(), @@ -242,7 +242,7 @@ func (m *Manager) SetLegacyManagement(isLegacy bool) error { } // Reset firewall to the default state -func (m *Manager) Reset(stateManager *statemanager.Manager) error { +func (m *Manager) Close(stateManager *statemanager.Manager) error { m.mutex.Lock() defer m.mutex.Unlock() diff --git a/client/firewall/nftables/manager_linux_test.go b/client/firewall/nftables/manager_linux_test.go index eaa8ef1f5..9ca20889b 100644 --- a/client/firewall/nftables/manager_linux_test.go +++ b/client/firewall/nftables/manager_linux_test.go @@ -65,7 +65,7 @@ func TestNftablesManager(t *testing.T) { time.Sleep(time.Second * 3) defer func() { - err = manager.Reset(nil) + err = manager.Close(nil) require.NoError(t, err, "failed to reset") time.Sleep(time.Second) }() @@ -162,7 +162,7 @@ func TestNftablesManager(t *testing.T) { // established rule remains require.Len(t, rules, 1, "expected 1 rules after deletion") - err = manager.Reset(nil) + err = manager.Close(nil) require.NoError(t, err, "failed to reset") } @@ -191,7 +191,7 @@ func TestNFtablesCreatePerformance(t *testing.T) { time.Sleep(time.Second * 3) defer func() { - if err := manager.Reset(nil); err != nil { + if err := manager.Close(nil); err != nil { t.Errorf("clear the manager state: %v", err) } time.Sleep(time.Second) @@ -274,7 +274,7 @@ func TestNftablesManagerCompatibilityWithIptables(t *testing.T) { require.NoError(t, manager.Init(nil)) t.Cleanup(func() { - err := manager.Reset(nil) + err := manager.Close(nil) require.NoError(t, err, "failed to reset manager state") // Verify iptables output after reset diff --git a/client/firewall/nftables/router_linux_test.go b/client/firewall/nftables/router_linux_test.go index 2a5d7168d..9081a8349 100644 --- a/client/firewall/nftables/router_linux_test.go +++ b/client/firewall/nftables/router_linux_test.go @@ -38,7 +38,7 @@ func TestNftablesManager_AddNatRule(t *testing.T) { // need fw manager to init both acl mgr and router for all chains to be present manager, err := Create(ifaceMock) t.Cleanup(func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }) require.NoError(t, err) require.NoError(t, manager.Init(nil)) @@ -127,7 +127,7 @@ func TestNftablesManager_RemoveNatRule(t *testing.T) { t.Run(testCase.Name, func(t *testing.T) { manager, err := Create(ifaceMock) t.Cleanup(func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }) require.NoError(t, err) require.NoError(t, manager.Init(nil)) diff --git a/client/firewall/nftables/state_linux.go b/client/firewall/nftables/state_linux.go index a68c8b8b8..facca1cec 100644 --- a/client/firewall/nftables/state_linux.go +++ b/client/firewall/nftables/state_linux.go @@ -39,7 +39,7 @@ func (s *ShutdownState) Cleanup() error { return fmt.Errorf("create nftables manager: %w", err) } - if err := nft.Reset(nil); err != nil { + if err := nft.Close(nil); err != nil { return fmt.Errorf("reset nftables manager: %w", err) } diff --git a/client/firewall/uspfilter/allow_netbird.go b/client/firewall/uspfilter/allow_netbird.go index 03f23f5e6..aba79bc21 100644 --- a/client/firewall/uspfilter/allow_netbird.go +++ b/client/firewall/uspfilter/allow_netbird.go @@ -8,12 +8,11 @@ import ( log "github.com/sirupsen/logrus" - "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack" "github.com/netbirdio/netbird/client/internal/statemanager" ) // Reset firewall to the default state -func (m *Manager) Reset(stateManager *statemanager.Manager) error { +func (m *Manager) Close(stateManager *statemanager.Manager) error { m.mutex.Lock() defer m.mutex.Unlock() @@ -22,17 +21,14 @@ func (m *Manager) Reset(stateManager *statemanager.Manager) error { if m.udpTracker != nil { m.udpTracker.Close() - m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout, m.logger) } if m.icmpTracker != nil { m.icmpTracker.Close() - m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout, m.logger) } if m.tcpTracker != nil { m.tcpTracker.Close() - m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout, m.logger) } if m.forwarder != nil { @@ -48,7 +44,7 @@ func (m *Manager) Reset(stateManager *statemanager.Manager) error { } if m.nativeFirewall != nil { - return m.nativeFirewall.Reset(stateManager) + return m.nativeFirewall.Close(stateManager) } return nil } diff --git a/client/firewall/uspfilter/allow_netbird_windows.go b/client/firewall/uspfilter/allow_netbird_windows.go index 379585978..ee540cb1d 100644 --- a/client/firewall/uspfilter/allow_netbird_windows.go +++ b/client/firewall/uspfilter/allow_netbird_windows.go @@ -9,7 +9,6 @@ import ( log "github.com/sirupsen/logrus" - "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack" "github.com/netbirdio/netbird/client/internal/statemanager" ) @@ -21,8 +20,8 @@ const ( firewallRuleName = "Netbird" ) -// Reset firewall to the default state -func (m *Manager) Reset(*statemanager.Manager) error { +// Close closes the firewall manager +func (m *Manager) Close(*statemanager.Manager) error { m.mutex.Lock() defer m.mutex.Unlock() @@ -31,17 +30,14 @@ func (m *Manager) Reset(*statemanager.Manager) error { if m.udpTracker != nil { m.udpTracker.Close() - m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout, m.logger) } if m.icmpTracker != nil { m.icmpTracker.Close() - m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout, m.logger) } if m.tcpTracker != nil { m.tcpTracker.Close() - m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout, m.logger) } if m.forwarder != nil { diff --git a/client/firewall/uspfilter/uspfilter_bench_test.go b/client/firewall/uspfilter/uspfilter_bench_test.go index 875bb2425..bb42a8052 100644 --- a/client/firewall/uspfilter/uspfilter_bench_test.go +++ b/client/firewall/uspfilter/uspfilter_bench_test.go @@ -160,7 +160,7 @@ func BenchmarkCoreFiltering(b *testing.B) { SetFilterFunc: func(device.PacketFilter) error { return nil }, }, false) defer b.Cleanup(func() { - require.NoError(b, manager.Reset(nil)) + require.NoError(b, manager.Close(nil)) }) manager.wgNetwork = &net.IPNet{ @@ -205,7 +205,7 @@ func BenchmarkStateScaling(b *testing.B) { SetFilterFunc: func(device.PacketFilter) error { return nil }, }, false) b.Cleanup(func() { - require.NoError(b, manager.Reset(nil)) + require.NoError(b, manager.Close(nil)) }) manager.wgNetwork = &net.IPNet{ @@ -253,7 +253,7 @@ func BenchmarkEstablishmentOverhead(b *testing.B) { SetFilterFunc: func(device.PacketFilter) error { return nil }, }, false) b.Cleanup(func() { - require.NoError(b, manager.Reset(nil)) + require.NoError(b, manager.Close(nil)) }) manager.wgNetwork = &net.IPNet{ @@ -452,7 +452,7 @@ func BenchmarkRoutedNetworkReturn(b *testing.B) { SetFilterFunc: func(device.PacketFilter) error { return nil }, }, false) b.Cleanup(func() { - require.NoError(b, manager.Reset(nil)) + require.NoError(b, manager.Close(nil)) }) // Setup scenario @@ -579,7 +579,7 @@ func BenchmarkLongLivedConnections(b *testing.B) { SetFilterFunc: func(device.PacketFilter) error { return nil }, }, false) defer b.Cleanup(func() { - require.NoError(b, manager.Reset(nil)) + require.NoError(b, manager.Close(nil)) }) manager.SetNetwork(&net.IPNet{ @@ -670,7 +670,7 @@ func BenchmarkShortLivedConnections(b *testing.B) { SetFilterFunc: func(device.PacketFilter) error { return nil }, }, false) defer b.Cleanup(func() { - require.NoError(b, manager.Reset(nil)) + require.NoError(b, manager.Close(nil)) }) manager.SetNetwork(&net.IPNet{ @@ -789,7 +789,7 @@ func BenchmarkParallelLongLivedConnections(b *testing.B) { SetFilterFunc: func(device.PacketFilter) error { return nil }, }, false) defer b.Cleanup(func() { - require.NoError(b, manager.Reset(nil)) + require.NoError(b, manager.Close(nil)) }) manager.SetNetwork(&net.IPNet{ @@ -877,7 +877,7 @@ func BenchmarkParallelShortLivedConnections(b *testing.B) { SetFilterFunc: func(device.PacketFilter) error { return nil }, }, false) defer b.Cleanup(func() { - require.NoError(b, manager.Reset(nil)) + require.NoError(b, manager.Close(nil)) }) manager.SetNetwork(&net.IPNet{ diff --git a/client/firewall/uspfilter/uspfilter_filter_test.go b/client/firewall/uspfilter/uspfilter_filter_test.go index 9a1456d00..9a5ec9c66 100644 --- a/client/firewall/uspfilter/uspfilter_filter_test.go +++ b/client/firewall/uspfilter/uspfilter_filter_test.go @@ -39,7 +39,7 @@ func TestPeerACLFiltering(t *testing.T) { require.NotNil(t, manager) t.Cleanup(func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }) manager.wgNetwork = wgNet @@ -310,7 +310,7 @@ func setupRoutedManager(tb testing.TB, network string) *Manager { require.False(tb, manager.nativeRouter) tb.Cleanup(func() { - require.NoError(tb, manager.Reset(nil)) + require.NoError(tb, manager.Close(nil)) }) return manager diff --git a/client/firewall/uspfilter/uspfilter_test.go b/client/firewall/uspfilter/uspfilter_test.go index 089bf8f55..c03762984 100644 --- a/client/firewall/uspfilter/uspfilter_test.go +++ b/client/firewall/uspfilter/uspfilter_test.go @@ -254,7 +254,7 @@ func TestManagerReset(t *testing.T) { return } - err = m.Reset(nil) + err = m.Close(nil) if err != nil { t.Errorf("failed to reset Manager: %v", err) return @@ -333,7 +333,7 @@ func TestNotMatchByIP(t *testing.T) { return } - if err = m.Reset(nil); err != nil { + if err = m.Close(nil); err != nil { t.Errorf("failed to reset Manager: %v", err) return } @@ -352,7 +352,7 @@ func TestRemovePacketHook(t *testing.T) { t.Fatalf("Failed to create Manager: %s", err) } defer func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }() // Add a UDP packet hook @@ -403,7 +403,7 @@ func TestProcessOutgoingHooks(t *testing.T) { manager.udpTracker.Close() manager.udpTracker = conntrack.NewUDPTracker(100*time.Millisecond, logger) defer func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }() manager.decoders = sync.Pool{ @@ -484,7 +484,7 @@ func TestUSPFilterCreatePerformance(t *testing.T) { time.Sleep(time.Second) defer func() { - if err := manager.Reset(nil); err != nil { + if err := manager.Close(nil); err != nil { t.Errorf("clear the manager state: %v", err) } time.Sleep(time.Second) @@ -530,7 +530,7 @@ func TestStatefulFirewall_UDPTracking(t *testing.T) { }, } defer func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }() // Set up packet parameters diff --git a/client/internal/acl/manager_test.go b/client/internal/acl/manager_test.go index 217dbce9f..0327d62ef 100644 --- a/client/internal/acl/manager_test.go +++ b/client/internal/acl/manager_test.go @@ -58,7 +58,7 @@ func TestDefaultManager(t *testing.T) { return } defer func(fw manager.Manager) { - _ = fw.Reset(nil) + _ = fw.Close(nil) }(fw) acl := NewDefaultManager(fw) @@ -352,7 +352,7 @@ func TestDefaultManagerEnableSSHRules(t *testing.T) { return } defer func(fw manager.Manager) { - _ = fw.Reset(nil) + _ = fw.Close(nil) }(fw) acl := NewDefaultManager(fw) diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index 94b87124b..d9886fcd8 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -1015,7 +1015,7 @@ func TestHandlerChain_DomainPriorities(t *testing.T) { mh.AssertExpectations(t) } - // Reset mocks + // Close mocks if mh, ok := tc.expectedHandler.(*MockHandler); ok { mh.ExpectedCalls = nil mh.Calls = nil diff --git a/client/internal/engine.go b/client/internal/engine.go index 10c4fb970..943b7cd0b 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -1362,7 +1362,7 @@ func (e *Engine) close() { } if e.firewall != nil { - err := e.firewall.Reset(e.stateManager) + err := e.firewall.Close(e.stateManager) if err != nil { log.Warnf("failed to reset firewall: %s", err) } From 60ffe0dc872b23595cdd0f6f69f3fa56ed56cf2f Mon Sep 17 00:00:00 2001 From: hakansa <43675540+hakansa@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:29:29 +0300 Subject: [PATCH 05/30] [client] UI Refactor Icon Paths (#3420) [client] UI Refactor Icon Paths (#3420) --- .github/workflows/release.yml | 4 +- .goreleaser_ui.yaml | 8 +- client/installer.nsis | 4 +- client/netbird.wxs | 2 +- client/resources.rc | 2 +- .../netbird-systemtray-connected-dark.ico | Bin .../netbird-systemtray-connected-dark.png | Bin .../netbird-systemtray-connected-macos.png | Bin .../netbird-systemtray-connected.ico | Bin .../netbird-systemtray-connected.png | Bin .../netbird-systemtray-connecting-dark.ico | Bin .../netbird-systemtray-connecting-dark.png | Bin .../netbird-systemtray-connecting-macos.png | Bin .../netbird-systemtray-connecting.ico | Bin .../netbird-systemtray-connecting.png | Bin .../netbird-systemtray-disconnected-macos.png | Bin .../netbird-systemtray-disconnected.ico | Bin .../netbird-systemtray-disconnected.png | Bin .../netbird-systemtray-error-dark.ico | Bin .../netbird-systemtray-error-dark.png | Bin .../netbird-systemtray-error-macos.png | Bin .../{ => assets}/netbird-systemtray-error.ico | Bin .../{ => assets}/netbird-systemtray-error.png | Bin ...tbird-systemtray-update-connected-dark.ico | Bin ...tbird-systemtray-update-connected-dark.png | Bin ...bird-systemtray-update-connected-macos.png | Bin .../netbird-systemtray-update-connected.ico | Bin .../netbird-systemtray-update-connected.png | Bin ...rd-systemtray-update-disconnected-dark.ico | Bin ...rd-systemtray-update-disconnected-dark.png | Bin ...d-systemtray-update-disconnected-macos.png | Bin ...netbird-systemtray-update-disconnected.ico | Bin ...netbird-systemtray-update-disconnected.png | Bin client/ui/{ => assets}/netbird.ico | Bin client/ui/{ => assets}/netbird.png | Bin client/ui/{ => build}/banner.bmp | Bin client/ui/{ => build}/build-ui-linux.sh | 0 client/ui/{ => build}/netbird.desktop | 0 client/ui/client_ui.go | 189 ++++++++++-------- client/ui/const.go | 15 ++ client/ui/icons.go | 24 +-- client/ui/icons_windows.go | 26 +-- client/ui/network.go | 2 +- client/ui/{ => process}/process.go | 4 +- client/ui/{ => process}/process_nonwindows.go | 2 +- client/ui/{ => process}/process_windows.go | 2 +- 46 files changed, 160 insertions(+), 124 deletions(-) rename client/ui/{ => assets}/netbird-systemtray-connected-dark.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-connected-dark.png (100%) rename client/ui/{ => assets}/netbird-systemtray-connected-macos.png (100%) rename client/ui/{ => assets}/netbird-systemtray-connected.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-connected.png (100%) rename client/ui/{ => assets}/netbird-systemtray-connecting-dark.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-connecting-dark.png (100%) rename client/ui/{ => assets}/netbird-systemtray-connecting-macos.png (100%) rename client/ui/{ => assets}/netbird-systemtray-connecting.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-connecting.png (100%) rename client/ui/{ => assets}/netbird-systemtray-disconnected-macos.png (100%) rename client/ui/{ => assets}/netbird-systemtray-disconnected.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-disconnected.png (100%) rename client/ui/{ => assets}/netbird-systemtray-error-dark.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-error-dark.png (100%) rename client/ui/{ => assets}/netbird-systemtray-error-macos.png (100%) rename client/ui/{ => assets}/netbird-systemtray-error.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-error.png (100%) rename client/ui/{ => assets}/netbird-systemtray-update-connected-dark.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-update-connected-dark.png (100%) rename client/ui/{ => assets}/netbird-systemtray-update-connected-macos.png (100%) rename client/ui/{ => assets}/netbird-systemtray-update-connected.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-update-connected.png (100%) rename client/ui/{ => assets}/netbird-systemtray-update-disconnected-dark.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-update-disconnected-dark.png (100%) rename client/ui/{ => assets}/netbird-systemtray-update-disconnected-macos.png (100%) rename client/ui/{ => assets}/netbird-systemtray-update-disconnected.ico (100%) rename client/ui/{ => assets}/netbird-systemtray-update-disconnected.png (100%) rename client/ui/{ => assets}/netbird.ico (100%) rename client/ui/{ => assets}/netbird.png (100%) rename client/ui/{ => build}/banner.bmp (100%) rename client/ui/{ => build}/build-ui-linux.sh (100%) rename client/ui/{ => build}/netbird.desktop (100%) create mode 100644 client/ui/const.go rename client/ui/{ => process}/process.go (90%) rename client/ui/{ => process}/process_nonwindows.go (96%) rename client/ui/{ => process}/process_windows.go (96%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 04874bdf4..919351f18 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: - name: Install goversioninfo run: go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@233067e - name: Generate windows syso amd64 - run: goversioninfo -icon client/ui/netbird.ico -manifest client/manifest.xml -product-name ${{ env.PRODUCT_NAME }} -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/resources_windows_amd64.syso + run: goversioninfo -icon client/ui/assets/netbird.ico -manifest client/manifest.xml -product-name ${{ env.PRODUCT_NAME }} -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/resources_windows_amd64.syso - name: Run GoReleaser uses: goreleaser/goreleaser-action@v4 with: @@ -150,7 +150,7 @@ jobs: - name: Install goversioninfo run: go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@233067e - name: Generate windows syso amd64 - run: goversioninfo -64 -icon client/ui/netbird.ico -manifest client/ui/manifest.xml -product-name ${{ env.PRODUCT_NAME }}-"UI" -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/ui/resources_windows_amd64.syso + run: goversioninfo -64 -icon client/ui/assets/netbird.ico -manifest client/ui/manifest.xml -product-name ${{ env.PRODUCT_NAME }}-"UI" -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/ui/resources_windows_amd64.syso - name: Run GoReleaser uses: goreleaser/goreleaser-action@v4 diff --git a/.goreleaser_ui.yaml b/.goreleaser_ui.yaml index 1dd649d1b..459f204d3 100644 --- a/.goreleaser_ui.yaml +++ b/.goreleaser_ui.yaml @@ -53,9 +53,9 @@ nfpms: scripts: postinstall: "release_files/ui-post-install.sh" contents: - - src: client/ui/netbird.desktop + - src: client/ui/build/netbird.desktop dst: /usr/share/applications/netbird.desktop - - src: client/ui/netbird.png + - src: client/ui/assets/netbird.png dst: /usr/share/pixmaps/netbird.png dependencies: - netbird @@ -72,9 +72,9 @@ nfpms: scripts: postinstall: "release_files/ui-post-install.sh" contents: - - src: client/ui/netbird.desktop + - src: client/ui/build/netbird.desktop dst: /usr/share/applications/netbird.desktop - - src: client/ui/netbird.png + - src: client/ui/assets/netbird.png dst: /usr/share/pixmaps/netbird.png dependencies: - netbird diff --git a/client/installer.nsis b/client/installer.nsis index af942a868..743c81a6d 100644 --- a/client/installer.nsis +++ b/client/installer.nsis @@ -6,8 +6,8 @@ !define DESCRIPTION "A WireGuard®-based mesh network that connects your devices into a single private network" !define INSTALLER_NAME "netbird-installer.exe" !define MAIN_APP_EXE "Netbird" -!define ICON "ui\\netbird.ico" -!define BANNER "ui\\banner.bmp" +!define ICON "ui\\assets\\netbird.ico" +!define BANNER "ui\\build\\banner.bmp" !define LICENSE_DATA "..\\LICENSE" !define INSTALL_DIR "$PROGRAMFILES64\${APP_NAME}" diff --git a/client/netbird.wxs b/client/netbird.wxs index ee9ab667f..5e03a014d 100644 --- a/client/netbird.wxs +++ b/client/netbird.wxs @@ -71,7 +71,7 @@ - + diff --git a/client/resources.rc b/client/resources.rc index ac411245e..696fd0dfa 100644 --- a/client/resources.rc +++ b/client/resources.rc @@ -5,5 +5,5 @@ #define STRINGIZE(x) #x #define EXPAND(x) STRINGIZE(x) CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml -7 ICON ui/netbird.ico +7 ICON ui/assets/netbird.ico wintun.dll RCDATA wintun.dll diff --git a/client/ui/netbird-systemtray-connected-dark.ico b/client/ui/assets/netbird-systemtray-connected-dark.ico similarity index 100% rename from client/ui/netbird-systemtray-connected-dark.ico rename to client/ui/assets/netbird-systemtray-connected-dark.ico diff --git a/client/ui/netbird-systemtray-connected-dark.png b/client/ui/assets/netbird-systemtray-connected-dark.png similarity index 100% rename from client/ui/netbird-systemtray-connected-dark.png rename to client/ui/assets/netbird-systemtray-connected-dark.png diff --git a/client/ui/netbird-systemtray-connected-macos.png b/client/ui/assets/netbird-systemtray-connected-macos.png similarity index 100% rename from client/ui/netbird-systemtray-connected-macos.png rename to client/ui/assets/netbird-systemtray-connected-macos.png diff --git a/client/ui/netbird-systemtray-connected.ico b/client/ui/assets/netbird-systemtray-connected.ico similarity index 100% rename from client/ui/netbird-systemtray-connected.ico rename to client/ui/assets/netbird-systemtray-connected.ico diff --git a/client/ui/netbird-systemtray-connected.png b/client/ui/assets/netbird-systemtray-connected.png similarity index 100% rename from client/ui/netbird-systemtray-connected.png rename to client/ui/assets/netbird-systemtray-connected.png diff --git a/client/ui/netbird-systemtray-connecting-dark.ico b/client/ui/assets/netbird-systemtray-connecting-dark.ico similarity index 100% rename from client/ui/netbird-systemtray-connecting-dark.ico rename to client/ui/assets/netbird-systemtray-connecting-dark.ico diff --git a/client/ui/netbird-systemtray-connecting-dark.png b/client/ui/assets/netbird-systemtray-connecting-dark.png similarity index 100% rename from client/ui/netbird-systemtray-connecting-dark.png rename to client/ui/assets/netbird-systemtray-connecting-dark.png diff --git a/client/ui/netbird-systemtray-connecting-macos.png b/client/ui/assets/netbird-systemtray-connecting-macos.png similarity index 100% rename from client/ui/netbird-systemtray-connecting-macos.png rename to client/ui/assets/netbird-systemtray-connecting-macos.png diff --git a/client/ui/netbird-systemtray-connecting.ico b/client/ui/assets/netbird-systemtray-connecting.ico similarity index 100% rename from client/ui/netbird-systemtray-connecting.ico rename to client/ui/assets/netbird-systemtray-connecting.ico diff --git a/client/ui/netbird-systemtray-connecting.png b/client/ui/assets/netbird-systemtray-connecting.png similarity index 100% rename from client/ui/netbird-systemtray-connecting.png rename to client/ui/assets/netbird-systemtray-connecting.png diff --git a/client/ui/netbird-systemtray-disconnected-macos.png b/client/ui/assets/netbird-systemtray-disconnected-macos.png similarity index 100% rename from client/ui/netbird-systemtray-disconnected-macos.png rename to client/ui/assets/netbird-systemtray-disconnected-macos.png diff --git a/client/ui/netbird-systemtray-disconnected.ico b/client/ui/assets/netbird-systemtray-disconnected.ico similarity index 100% rename from client/ui/netbird-systemtray-disconnected.ico rename to client/ui/assets/netbird-systemtray-disconnected.ico diff --git a/client/ui/netbird-systemtray-disconnected.png b/client/ui/assets/netbird-systemtray-disconnected.png similarity index 100% rename from client/ui/netbird-systemtray-disconnected.png rename to client/ui/assets/netbird-systemtray-disconnected.png diff --git a/client/ui/netbird-systemtray-error-dark.ico b/client/ui/assets/netbird-systemtray-error-dark.ico similarity index 100% rename from client/ui/netbird-systemtray-error-dark.ico rename to client/ui/assets/netbird-systemtray-error-dark.ico diff --git a/client/ui/netbird-systemtray-error-dark.png b/client/ui/assets/netbird-systemtray-error-dark.png similarity index 100% rename from client/ui/netbird-systemtray-error-dark.png rename to client/ui/assets/netbird-systemtray-error-dark.png diff --git a/client/ui/netbird-systemtray-error-macos.png b/client/ui/assets/netbird-systemtray-error-macos.png similarity index 100% rename from client/ui/netbird-systemtray-error-macos.png rename to client/ui/assets/netbird-systemtray-error-macos.png diff --git a/client/ui/netbird-systemtray-error.ico b/client/ui/assets/netbird-systemtray-error.ico similarity index 100% rename from client/ui/netbird-systemtray-error.ico rename to client/ui/assets/netbird-systemtray-error.ico diff --git a/client/ui/netbird-systemtray-error.png b/client/ui/assets/netbird-systemtray-error.png similarity index 100% rename from client/ui/netbird-systemtray-error.png rename to client/ui/assets/netbird-systemtray-error.png diff --git a/client/ui/netbird-systemtray-update-connected-dark.ico b/client/ui/assets/netbird-systemtray-update-connected-dark.ico similarity index 100% rename from client/ui/netbird-systemtray-update-connected-dark.ico rename to client/ui/assets/netbird-systemtray-update-connected-dark.ico diff --git a/client/ui/netbird-systemtray-update-connected-dark.png b/client/ui/assets/netbird-systemtray-update-connected-dark.png similarity index 100% rename from client/ui/netbird-systemtray-update-connected-dark.png rename to client/ui/assets/netbird-systemtray-update-connected-dark.png diff --git a/client/ui/netbird-systemtray-update-connected-macos.png b/client/ui/assets/netbird-systemtray-update-connected-macos.png similarity index 100% rename from client/ui/netbird-systemtray-update-connected-macos.png rename to client/ui/assets/netbird-systemtray-update-connected-macos.png diff --git a/client/ui/netbird-systemtray-update-connected.ico b/client/ui/assets/netbird-systemtray-update-connected.ico similarity index 100% rename from client/ui/netbird-systemtray-update-connected.ico rename to client/ui/assets/netbird-systemtray-update-connected.ico diff --git a/client/ui/netbird-systemtray-update-connected.png b/client/ui/assets/netbird-systemtray-update-connected.png similarity index 100% rename from client/ui/netbird-systemtray-update-connected.png rename to client/ui/assets/netbird-systemtray-update-connected.png diff --git a/client/ui/netbird-systemtray-update-disconnected-dark.ico b/client/ui/assets/netbird-systemtray-update-disconnected-dark.ico similarity index 100% rename from client/ui/netbird-systemtray-update-disconnected-dark.ico rename to client/ui/assets/netbird-systemtray-update-disconnected-dark.ico diff --git a/client/ui/netbird-systemtray-update-disconnected-dark.png b/client/ui/assets/netbird-systemtray-update-disconnected-dark.png similarity index 100% rename from client/ui/netbird-systemtray-update-disconnected-dark.png rename to client/ui/assets/netbird-systemtray-update-disconnected-dark.png diff --git a/client/ui/netbird-systemtray-update-disconnected-macos.png b/client/ui/assets/netbird-systemtray-update-disconnected-macos.png similarity index 100% rename from client/ui/netbird-systemtray-update-disconnected-macos.png rename to client/ui/assets/netbird-systemtray-update-disconnected-macos.png diff --git a/client/ui/netbird-systemtray-update-disconnected.ico b/client/ui/assets/netbird-systemtray-update-disconnected.ico similarity index 100% rename from client/ui/netbird-systemtray-update-disconnected.ico rename to client/ui/assets/netbird-systemtray-update-disconnected.ico diff --git a/client/ui/netbird-systemtray-update-disconnected.png b/client/ui/assets/netbird-systemtray-update-disconnected.png similarity index 100% rename from client/ui/netbird-systemtray-update-disconnected.png rename to client/ui/assets/netbird-systemtray-update-disconnected.png diff --git a/client/ui/netbird.ico b/client/ui/assets/netbird.ico similarity index 100% rename from client/ui/netbird.ico rename to client/ui/assets/netbird.ico diff --git a/client/ui/netbird.png b/client/ui/assets/netbird.png similarity index 100% rename from client/ui/netbird.png rename to client/ui/assets/netbird.png diff --git a/client/ui/banner.bmp b/client/ui/build/banner.bmp similarity index 100% rename from client/ui/banner.bmp rename to client/ui/build/banner.bmp diff --git a/client/ui/build-ui-linux.sh b/client/ui/build/build-ui-linux.sh similarity index 100% rename from client/ui/build-ui-linux.sh rename to client/ui/build/build-ui-linux.sh diff --git a/client/ui/netbird.desktop b/client/ui/build/netbird.desktop similarity index 100% rename from client/ui/netbird.desktop rename to client/ui/build/netbird.desktop diff --git a/client/ui/client_ui.go b/client/ui/client_ui.go index 889db70d6..914d4f3b9 100644 --- a/client/ui/client_ui.go +++ b/client/ui/client_ui.go @@ -35,7 +35,9 @@ import ( "github.com/netbirdio/netbird/client/proto" "github.com/netbirdio/netbird/client/ui/desktop" "github.com/netbirdio/netbird/client/ui/event" + "github.com/netbirdio/netbird/client/ui/process" "github.com/netbirdio/netbird/util" + "github.com/netbirdio/netbird/version" ) @@ -44,94 +46,125 @@ const ( failFastTimeout = time.Second ) +const ( + censoredPreSharedKey = "**********" +) + func main() { - var daemonAddr string - - defaultDaemonAddr := "unix:///var/run/netbird.sock" - if runtime.GOOS == "windows" { - defaultDaemonAddr = "tcp://127.0.0.1:41731" - } - - flag.StringVar( - &daemonAddr, "daemon-addr", - defaultDaemonAddr, - "Daemon service address to serve CLI requests [unix|tcp]://[path|host:port]") - - var showSettings bool - flag.BoolVar(&showSettings, "settings", false, "run settings windows") - var showRoutes bool - flag.BoolVar(&showRoutes, "networks", false, "run networks windows") - var errorMSG string - flag.StringVar(&errorMSG, "error-msg", "", "displays a error message window") - - tmpDir := "/tmp" - if runtime.GOOS == "windows" { - tmpDir = os.TempDir() - } - - var saveLogsInFile bool - flag.BoolVar(&saveLogsInFile, "use-log-file", false, fmt.Sprintf("save logs in a file: %s/netbird-ui-PID.log", tmpDir)) - - flag.Parse() + daemonAddr, showSettings, showNetworks, errorMsg, saveLogsInFile := parseFlags() + // Initialize file logging if needed. if saveLogsInFile { - logFile := path.Join(tmpDir, fmt.Sprintf("netbird-ui-%d.log", os.Getpid())) - err := util.InitLog("trace", logFile) - if err != nil { + if err := initLogFile(); err != nil { log.Errorf("error while initializing log: %v", err) return } } + // Create the Fyne application. a := app.NewWithID("NetBird") a.SetIcon(fyne.NewStaticResource("netbird", iconDisconnected)) - if errorMSG != "" { - showErrorMSG(errorMSG) + // Show error message window if needed. + if errorMsg != "" { + showErrorMessage(errorMsg) return } - client := newServiceClient(daemonAddr, a, showSettings, showRoutes) + // Create the service client (this also builds the settings or networks UI if requested). + client := newServiceClient(daemonAddr, a, showSettings, showNetworks) + + // Watch for theme/settings changes to update the icon. + go watchSettingsChanges(a, client) + + // Run in window mode if any UI flag was set. + if showSettings || showNetworks { + a.Run() + return + } + + // Check for another running process. + running, err := process.IsAnotherProcessRunning() + if err != nil { + log.Errorf("error while checking process: %v", err) + return + } + if running { + log.Warn("another process is running") + return + } + + client.setDefaultFonts() + systray.Run(client.onTrayReady, client.onTrayExit) +} + +// parseFlags reads and returns all needed command-line flags. +func parseFlags() (daemonAddr string, showSettings, showNetworks bool, errorMsg string, saveLogsInFile bool) { + defaultDaemonAddr := "unix:///var/run/netbird.sock" + if runtime.GOOS == "windows" { + defaultDaemonAddr = "tcp://127.0.0.1:41731" + } + flag.StringVar(&daemonAddr, "daemon-addr", defaultDaemonAddr, "Daemon service address to serve CLI requests [unix|tcp]://[path|host:port]") + flag.BoolVar(&showSettings, "settings", false, "run settings window") + flag.BoolVar(&showNetworks, "networks", false, "run networks window") + flag.StringVar(&errorMsg, "error-msg", "", "displays an error message window") + + tmpDir := "/tmp" + if runtime.GOOS == "windows" { + tmpDir = os.TempDir() + } + flag.BoolVar(&saveLogsInFile, "use-log-file", false, fmt.Sprintf("save logs in a file: %s/netbird-ui-PID.log", tmpDir)) + flag.Parse() + return +} + +// initLogFile initializes logging into a file. +func initLogFile() error { + tmpDir := "/tmp" + if runtime.GOOS == "windows" { + tmpDir = os.TempDir() + } + logFile := path.Join(tmpDir, fmt.Sprintf("netbird-ui-%d.log", os.Getpid())) + return util.InitLog("trace", logFile) +} + +// watchSettingsChanges listens for Fyne theme/settings changes and updates the client icon. +func watchSettingsChanges(a fyne.App, client *serviceClient) { settingsChangeChan := make(chan fyne.Settings) a.Settings().AddChangeListener(settingsChangeChan) - go func() { - for range settingsChangeChan { - client.updateIcon() - } - }() - - if showSettings || showRoutes { - a.Run() - } else { - running, err := isAnotherProcessRunning() - if err != nil { - log.Errorf("error while checking process: %v", err) - } - if running { - log.Warn("another process is running") - return - } - client.setDefaultFonts() - systray.Run(client.onTrayReady, client.onTrayExit) + for range settingsChangeChan { + client.updateIcon() } } -//go:embed netbird-systemtray-connected-macos.png +// showErrorMessage displays an error message in a simple window. +func showErrorMessage(msg string) { + a := app.New() + w := a.NewWindow("NetBird Error") + label := widget.NewLabel(msg) + label.Wrapping = fyne.TextWrapWord + w.SetContent(label) + w.Resize(fyne.NewSize(400, 100)) + w.Show() + a.Run() +} + +//go:embed assets/netbird-systemtray-connected-macos.png var iconConnectedMacOS []byte -//go:embed netbird-systemtray-disconnected-macos.png +//go:embed assets/netbird-systemtray-disconnected-macos.png var iconDisconnectedMacOS []byte -//go:embed netbird-systemtray-update-disconnected-macos.png +//go:embed assets/netbird-systemtray-update-disconnected-macos.png var iconUpdateDisconnectedMacOS []byte -//go:embed netbird-systemtray-update-connected-macos.png +//go:embed assets/netbird-systemtray-update-connected-macos.png var iconUpdateConnectedMacOS []byte -//go:embed netbird-systemtray-connecting-macos.png +//go:embed assets/netbird-systemtray-connecting-macos.png var iconConnectingMacOS []byte -//go:embed netbird-systemtray-error-macos.png +//go:embed assets/netbird-systemtray-error-macos.png var iconErrorMacOS []byte type serviceClient struct { @@ -301,18 +334,6 @@ func (s *serviceClient) showSettingsUI() { s.wSettings.Show() } -// showErrorMSG opens a fyne app window to display the supplied message -func showErrorMSG(msg string) { - app := app.New() - w := app.NewWindow("NetBird Error") - content := widget.NewLabel(msg) - content.Wrapping = fyne.TextWrapWord - w.SetContent(content) - w.Resize(fyne.NewSize(400, 100)) - w.Show() - app.Run() -} - // getSettingsForm to embed it into settings window. func (s *serviceClient) getSettingsForm() *widget.Form { return &widget.Form{ @@ -328,7 +349,7 @@ func (s *serviceClient) getSettingsForm() *widget.Form { }, SubmitText: "Save", OnSubmit: func() { - if s.iPreSharedKey.Text != "" && s.iPreSharedKey.Text != "**********" { + if s.iPreSharedKey.Text != "" && s.iPreSharedKey.Text != censoredPreSharedKey { // validate preSharedKey if it added if _, err := wgtypes.ParseKey(s.iPreSharedKey.Text); err != nil { dialog.ShowError(fmt.Errorf("Invalid Pre-shared Key Value"), s.wSettings) @@ -366,7 +387,7 @@ func (s *serviceClient) getSettingsForm() *widget.Form { WireguardPort: &port, } - if s.iPreSharedKey.Text != "**********" { + if s.iPreSharedKey.Text != censoredPreSharedKey { loginRequest.OptionalPreSharedKey = &s.iPreSharedKey.Text } @@ -588,21 +609,21 @@ func (s *serviceClient) onTrayReady() { s.mAdminPanel = systray.AddMenuItem("Admin Panel", "Netbird Admin Panel") systray.AddSeparator() - s.mSettings = systray.AddMenuItem("Settings", "Settings of the application") - s.mAllowSSH = s.mSettings.AddSubMenuItemCheckbox("Allow SSH", "Allow SSH connections", false) - s.mAutoConnect = s.mSettings.AddSubMenuItemCheckbox("Connect on Startup", "Connect automatically when the service starts", false) - s.mEnableRosenpass = s.mSettings.AddSubMenuItemCheckbox("Enable Quantum-Resistance", "Enable post-quantum security via Rosenpass", false) - s.mNotifications = s.mSettings.AddSubMenuItemCheckbox("Notifications", "Enable notifications", false) - s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", "Advanced settings of the application") - s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", "Create and open debug information bundle") + s.mSettings = systray.AddMenuItem("Settings", settingsMenuDescr) + s.mAllowSSH = s.mSettings.AddSubMenuItemCheckbox("Allow SSH", allowSSHMenuDescr, false) + s.mAutoConnect = s.mSettings.AddSubMenuItemCheckbox("Connect on Startup", autoConnectMenuDescr, false) + s.mEnableRosenpass = s.mSettings.AddSubMenuItemCheckbox("Enable Quantum-Resistance", quantumResistanceMenuDescr, false) + s.mNotifications = s.mSettings.AddSubMenuItemCheckbox("Notifications", notificationsMenuDescr, false) + s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", advancedSettingsMenuDescr) + s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", debugBundleMenuDescr) s.loadSettings() s.exitNodeMu.Lock() - s.mExitNode = systray.AddMenuItem("Exit Node", "Select exit node for routing traffic") + s.mExitNode = systray.AddMenuItem("Exit Node", exitNodeMenuDescr) s.mExitNode.Disable() s.exitNodeMu.Unlock() - s.mNetworks = systray.AddMenuItem("Networks", "Open the networks management window") + s.mNetworks = systray.AddMenuItem("Networks", networksMenuDescr) s.mNetworks.Disable() systray.AddSeparator() @@ -619,11 +640,11 @@ func (s *serviceClient) onTrayReady() { s.mVersionDaemon.Disable() s.mVersionDaemon.Hide() - s.mUpdate = s.mAbout.AddSubMenuItem("Download latest version", "Download latest version") + s.mUpdate = s.mAbout.AddSubMenuItem("Download latest version", latestVersionMenuDescr) s.mUpdate.Hide() systray.AddSeparator() - s.mQuit = systray.AddMenuItem("Quit", "Quit the client app") + s.mQuit = systray.AddMenuItem("Quit", quitMenuDescr) // update exit node menu in case service is already connected go s.updateExitNodes() diff --git a/client/ui/const.go b/client/ui/const.go new file mode 100644 index 000000000..0253750d1 --- /dev/null +++ b/client/ui/const.go @@ -0,0 +1,15 @@ +package main + +const ( + settingsMenuDescr = "Settings of the application" + allowSSHMenuDescr = "Allow SSH connections" + autoConnectMenuDescr = "Connect automatically when the service starts" + quantumResistanceMenuDescr = "Enable post-quantum security via Rosenpass" + notificationsMenuDescr = "Enable notifications" + advancedSettingsMenuDescr = "Advanced settings of the application" + debugBundleMenuDescr = "Create and open debug information bundle" + exitNodeMenuDescr = "Select exit node for routing traffic" + networksMenuDescr = "Open the networks management window" + latestVersionMenuDescr = "Download latest version" + quitMenuDescr = "Quit the client app" +) diff --git a/client/ui/icons.go b/client/ui/icons.go index 6f3a9dbc9..e88fb9378 100644 --- a/client/ui/icons.go +++ b/client/ui/icons.go @@ -6,38 +6,38 @@ import ( _ "embed" ) -//go:embed netbird.png +//go:embed assets/netbird.png var iconAbout []byte -//go:embed netbird-systemtray-connected.png +//go:embed assets/netbird-systemtray-connected.png var iconConnected []byte -//go:embed netbird-systemtray-connected-dark.png +//go:embed assets/netbird-systemtray-connected-dark.png var iconConnectedDark []byte -//go:embed netbird-systemtray-disconnected.png +//go:embed assets/netbird-systemtray-disconnected.png var iconDisconnected []byte -//go:embed netbird-systemtray-update-disconnected.png +//go:embed assets/netbird-systemtray-update-disconnected.png var iconUpdateDisconnected []byte -//go:embed netbird-systemtray-update-disconnected-dark.png +//go:embed assets/netbird-systemtray-update-disconnected-dark.png var iconUpdateDisconnectedDark []byte -//go:embed netbird-systemtray-update-connected.png +//go:embed assets/netbird-systemtray-update-connected.png var iconUpdateConnected []byte -//go:embed netbird-systemtray-update-connected-dark.png +//go:embed assets/netbird-systemtray-update-connected-dark.png var iconUpdateConnectedDark []byte -//go:embed netbird-systemtray-connecting.png +//go:embed assets/netbird-systemtray-connecting.png var iconConnecting []byte -//go:embed netbird-systemtray-connecting-dark.png +//go:embed assets/netbird-systemtray-connecting-dark.png var iconConnectingDark []byte -//go:embed netbird-systemtray-error.png +//go:embed assets/netbird-systemtray-error.png var iconError []byte -//go:embed netbird-systemtray-error-dark.png +//go:embed assets/netbird-systemtray-error-dark.png var iconErrorDark []byte diff --git a/client/ui/icons_windows.go b/client/ui/icons_windows.go index a2a924763..2107d3852 100644 --- a/client/ui/icons_windows.go +++ b/client/ui/icons_windows.go @@ -1,41 +1,41 @@ package main import ( - _ "embed" + _ "embed" ) -//go:embed netbird.ico +//go:embed assets/netbird.ico var iconAbout []byte -//go:embed netbird-systemtray-connected.ico +//go:embed assets/netbird-systemtray-connected.ico var iconConnected []byte -//go:embed netbird-systemtray-connected-dark.ico +//go:embed assets/netbird-systemtray-connected-dark.ico var iconConnectedDark []byte -//go:embed netbird-systemtray-disconnected.ico +//go:embed assets/netbird-systemtray-disconnected.ico var iconDisconnected []byte -//go:embed netbird-systemtray-update-disconnected.ico +//go:embed assets/netbird-systemtray-update-disconnected.ico var iconUpdateDisconnected []byte -//go:embed netbird-systemtray-update-disconnected-dark.ico +//go:embed assets/netbird-systemtray-update-disconnected-dark.ico var iconUpdateDisconnectedDark []byte -//go:embed netbird-systemtray-update-connected.ico +//go:embed assets/netbird-systemtray-update-connected.ico var iconUpdateConnected []byte -//go:embed netbird-systemtray-update-connected-dark.ico +//go:embed assets/netbird-systemtray-update-connected-dark.ico var iconUpdateConnectedDark []byte -//go:embed netbird-systemtray-connecting.ico +//go:embed assets/netbird-systemtray-connecting.ico var iconConnecting []byte -//go:embed netbird-systemtray-connecting-dark.ico +//go:embed assets/netbird-systemtray-connecting-dark.ico var iconConnectingDark []byte -//go:embed netbird-systemtray-error.ico +//go:embed assets/netbird-systemtray-error.ico var iconError []byte -//go:embed netbird-systemtray-error-dark.ico +//go:embed assets/netbird-systemtray-error-dark.ico var iconErrorDark []byte diff --git a/client/ui/network.go b/client/ui/network.go index 750788cf3..b21554f09 100644 --- a/client/ui/network.go +++ b/client/ui/network.go @@ -363,7 +363,7 @@ func (s *serviceClient) recreateExitNodeMenu(exitNodes []*proto.Network) { if runtime.GOOS == "linux" || runtime.GOOS == "freebsd" { s.mExitNode.Remove() - s.mExitNode = systray.AddMenuItem("Exit Node", "Select exit node for routing traffic") + s.mExitNode = systray.AddMenuItem("Exit Node", exitNodeMenuDescr) } for _, node := range exitNodes { diff --git a/client/ui/process.go b/client/ui/process/process.go similarity index 90% rename from client/ui/process.go rename to client/ui/process/process.go index bcb3dd879..f9a8a4fe9 100644 --- a/client/ui/process.go +++ b/client/ui/process/process.go @@ -1,4 +1,4 @@ -package main +package process import ( "os" @@ -8,7 +8,7 @@ import ( "github.com/shirou/gopsutil/v3/process" ) -func isAnotherProcessRunning() (bool, error) { +func IsAnotherProcessRunning() (bool, error) { processes, err := process.Processes() if err != nil { return false, err diff --git a/client/ui/process_nonwindows.go b/client/ui/process/process_nonwindows.go similarity index 96% rename from client/ui/process_nonwindows.go rename to client/ui/process/process_nonwindows.go index 0d17be2be..cf9f6443d 100644 --- a/client/ui/process_nonwindows.go +++ b/client/ui/process/process_nonwindows.go @@ -1,6 +1,6 @@ //go:build !windows -package main +package process import ( "os" diff --git a/client/ui/process_windows.go b/client/ui/process/process_windows.go similarity index 96% rename from client/ui/process_windows.go rename to client/ui/process/process_windows.go index b15b0ed24..2d211d1a4 100644 --- a/client/ui/process_windows.go +++ b/client/ui/process/process_windows.go @@ -1,4 +1,4 @@ -package main +package process import ( "os/user" From c111675dd8788ef8ab662989b1e5a90fc04a9a56 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Wed, 5 Mar 2025 18:57:17 +0100 Subject: [PATCH 06/30] [client] Handle large DNS packets in dns route resolution (#3441) --- client/internal/routemanager/dnsinterceptor/handler.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/internal/routemanager/dnsinterceptor/handler.go b/client/internal/routemanager/dnsinterceptor/handler.go index f36285cc4..da1056e2d 100644 --- a/client/internal/routemanager/dnsinterceptor/handler.go +++ b/client/internal/routemanager/dnsinterceptor/handler.go @@ -160,6 +160,12 @@ func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { return } + // set the AuthenticatedData flag and the EDNS0 buffer size to 4096 bytes to support larger dns records + if r.Extra == nil { + r.SetEdns0(4096, false) + r.MsgHdr.AuthenticatedData = true + } + client := &dns.Client{ Timeout: 5 * time.Second, Net: "udp", From 062d1ec76f6b9fba60590a297f4063b713d6e6d0 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Thu, 6 Mar 2025 01:10:37 +0100 Subject: [PATCH 07/30] [misc] Update bug-issue-report.md template (#3449) --- .github/ISSUE_TEMPLATE/bug-issue-report.md | 25 +++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue-report.md b/.github/ISSUE_TEMPLATE/bug-issue-report.md index 87f757f42..3633cca4f 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue-report.md +++ b/.github/ISSUE_TEMPLATE/bug-issue-report.md @@ -31,14 +31,22 @@ Please specify whether you use NetBird Cloud or self-host NetBird's control plan `netbird version` -**NetBird status -dA output:** +**Is any other VPN software installed?** -If applicable, add the `netbird status -dA' command output. +If yes, which one? -**Do you face any (non-mobile) client issues?** +**Debug output** -Please provide the file created by `netbird debug for 1m -AS`. -We advise reviewing the anonymized files for any remaining PII. +To help us resolve the problem, please attach the following debug output + + netbird status -dA + +As well as the file created by + + netbird debug for 1m -AS + + +We advise reviewing the anonymized output for any remaining personal information. **Screenshots** @@ -47,3 +55,10 @@ If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. + +**Have you tried these troubleshooting steps?** +- [ ] Checked for newer NetBird versions +- [ ] Searched for similar issues on GitHub (including closed ones) +- [ ] Restarted the NetBird client +- [ ] Disabled other VPN software +- [ ] Checked firewall settings From 4b76d93cecedecf0985f805a164b5a00a04d1b56 Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Fri, 7 Mar 2025 12:00:25 +0100 Subject: [PATCH 08/30] [client] Fix TURN-Relay switch (#3456) - When a peer is connected with TURN and a Relay connection is established, do not force switching to Relay. Keep using TURN until disconnection. -In the proxy preparation phase, the Bind Proxy does not set the remote conn as a fake address for Bind. When running the Work() function, the proper proxy instance updates the conn inside the Bind. --- client/iface/bind/ice_bind.go | 44 +++--------------------- client/iface/wgproxy/bind/proxy.go | 54 ++++++++++++++++++++---------- client/internal/peer/conn.go | 8 ++--- 3 files changed, 45 insertions(+), 61 deletions(-) diff --git a/client/iface/bind/ice_bind.go b/client/iface/bind/ice_bind.go index 41f415af7..6897f04a1 100644 --- a/client/iface/bind/ice_bind.go +++ b/client/iface/bind/ice_bind.go @@ -5,7 +5,6 @@ import ( "net" "net/netip" "runtime" - "strings" "sync" "github.com/pion/stun/v2" @@ -108,35 +107,17 @@ func (s *ICEBind) GetICEMux() (*UniversalUDPMuxDefault, error) { return s.udpMux, nil } -func (b *ICEBind) SetEndpoint(peerAddress *net.UDPAddr, conn net.Conn) (*net.UDPAddr, error) { - fakeUDPAddr, err := fakeAddress(peerAddress) - if err != nil { - return nil, err - } - - // force IPv4 - fakeAddr, ok := netip.AddrFromSlice(fakeUDPAddr.IP.To4()) - if !ok { - return nil, fmt.Errorf("failed to convert IP to netip.Addr") - } - +func (b *ICEBind) SetEndpoint(fakeIP netip.Addr, conn net.Conn) { b.endpointsMu.Lock() - b.endpoints[fakeAddr] = conn + b.endpoints[fakeIP] = conn b.endpointsMu.Unlock() - - return fakeUDPAddr, nil } -func (b *ICEBind) RemoveEndpoint(fakeUDPAddr *net.UDPAddr) { - fakeAddr, ok := netip.AddrFromSlice(fakeUDPAddr.IP.To4()) - if !ok { - log.Warnf("failed to convert IP to netip.Addr") - return - } - +func (b *ICEBind) RemoveEndpoint(fakeIP netip.Addr) { b.endpointsMu.Lock() defer b.endpointsMu.Unlock() - delete(b.endpoints, fakeAddr) + + delete(b.endpoints, fakeIP) } func (b *ICEBind) Send(bufs [][]byte, ep wgConn.Endpoint) error { @@ -275,21 +256,6 @@ func (c *ICEBind) receiveRelayed(buffs [][]byte, sizes []int, eps []wgConn.Endpo } } -// fakeAddress returns a fake address that is used to as an identifier for the peer. -// The fake address is in the format of 127.1.x.x where x.x is the last two octets of the peer address. -func fakeAddress(peerAddress *net.UDPAddr) (*net.UDPAddr, error) { - octets := strings.Split(peerAddress.IP.String(), ".") - if len(octets) != 4 { - return nil, fmt.Errorf("invalid IP format") - } - - newAddr := &net.UDPAddr{ - IP: net.ParseIP(fmt.Sprintf("127.1.%s.%s", octets[2], octets[3])), - Port: peerAddress.Port, - } - return newAddr, nil -} - func getMessages(msgsPool *sync.Pool) *[]ipv6.Message { return msgsPool.Get().(*[]ipv6.Message) } diff --git a/client/iface/wgproxy/bind/proxy.go b/client/iface/wgproxy/bind/proxy.go index 8a2e65382..614787e17 100644 --- a/client/iface/wgproxy/bind/proxy.go +++ b/client/iface/wgproxy/bind/proxy.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "net/netip" + "strings" "sync" log "github.com/sirupsen/logrus" @@ -16,13 +17,13 @@ import ( type ProxyBind struct { Bind *bind.ICEBind - wgAddr *net.UDPAddr - wgEndpoint *bind.Endpoint - remoteConn net.Conn - ctx context.Context - cancel context.CancelFunc - closeMu sync.Mutex - closed bool + fakeNetIP *netip.AddrPort + wgBindEndpoint *bind.Endpoint + remoteConn net.Conn + ctx context.Context + cancel context.CancelFunc + closeMu sync.Mutex + closed bool pausedMu sync.Mutex paused bool @@ -33,20 +34,24 @@ type ProxyBind struct { // endpoint is the NetBird address of the remote peer. The SetEndpoint return with the address what will be used in the // WireGuard configuration. func (p *ProxyBind) AddTurnConn(ctx context.Context, nbAddr *net.UDPAddr, remoteConn net.Conn) error { - addr, err := p.Bind.SetEndpoint(nbAddr, remoteConn) + fakeNetIP, err := fakeAddress(nbAddr) if err != nil { return err } - p.wgAddr = addr - p.wgEndpoint = addrToEndpoint(addr) + p.fakeNetIP = fakeNetIP + p.wgBindEndpoint = &bind.Endpoint{AddrPort: *fakeNetIP} p.remoteConn = remoteConn p.ctx, p.cancel = context.WithCancel(ctx) - return err + return nil } func (p *ProxyBind) EndpointAddr() *net.UDPAddr { - return p.wgAddr + return &net.UDPAddr{ + IP: p.fakeNetIP.Addr().AsSlice(), + Port: int(p.fakeNetIP.Port()), + Zone: p.fakeNetIP.Addr().Zone(), + } } func (p *ProxyBind) Work() { @@ -54,6 +59,8 @@ func (p *ProxyBind) Work() { return } + p.Bind.SetEndpoint(p.fakeNetIP.Addr(), p.remoteConn) + p.pausedMu.Lock() p.paused = false p.pausedMu.Unlock() @@ -93,7 +100,7 @@ func (p *ProxyBind) close() error { p.cancel() - p.Bind.RemoveEndpoint(p.wgAddr) + p.Bind.RemoveEndpoint(p.fakeNetIP.Addr()) if rErr := p.remoteConn.Close(); rErr != nil && !errors.Is(rErr, net.ErrClosed) { return rErr @@ -126,7 +133,7 @@ func (p *ProxyBind) proxyToLocal(ctx context.Context) { } msg := bind.RecvMessage{ - Endpoint: p.wgEndpoint, + Endpoint: p.wgBindEndpoint, Buffer: buf[:n], } p.Bind.RecvChan <- msg @@ -134,8 +141,19 @@ func (p *ProxyBind) proxyToLocal(ctx context.Context) { } } -func addrToEndpoint(addr *net.UDPAddr) *bind.Endpoint { - ip, _ := netip.AddrFromSlice(addr.IP.To4()) - addrPort := netip.AddrPortFrom(ip, uint16(addr.Port)) - return &bind.Endpoint{AddrPort: addrPort} +// fakeAddress returns a fake address that is used to as an identifier for the peer. +// The fake address is in the format of 127.1.x.x where x.x is the last two octets of the peer address. +func fakeAddress(peerAddress *net.UDPAddr) (*netip.AddrPort, error) { + octets := strings.Split(peerAddress.IP.String(), ".") + if len(octets) != 4 { + return nil, fmt.Errorf("invalid IP format") + } + + fakeIP, err := netip.ParseAddr(fmt.Sprintf("127.1.%s.%s", octets[2], octets[3])) + if err != nil { + return nil, fmt.Errorf("failed to parse new IP: %w", err) + } + + netipAddr := netip.AddrPortFrom(fakeIP, uint16(peerAddress.Port)) + return &netipAddr, nil } diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index 9b4d1a554..b91cfe33c 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -442,8 +442,8 @@ func (conn *Conn) onRelayConnectionIsReady(rci RelayConnInfo) { conn.log.Infof("created new wgProxy for relay connection: %s", wgProxy.EndpointAddr().String()) - if conn.iceP2PIsActive() { - conn.log.Debugf("do not switch to relay because current priority is: %s", conn.currentConnPriority.String()) + if conn.isICEActive() { + conn.log.Infof("do not switch to relay because current priority is: %s", conn.currentConnPriority.String()) conn.setRelayedProxy(wgProxy) conn.statusRelay.Set(StatusConnected) conn.updateRelayStatus(rci.relayedConn.RemoteAddr().String(), rci.rosenpassPubKey) @@ -711,8 +711,8 @@ func (conn *Conn) isReadyToUpgrade() bool { return conn.wgProxyRelay != nil && conn.currentConnPriority != connPriorityRelay } -func (conn *Conn) iceP2PIsActive() bool { - return conn.currentConnPriority == connPriorityICEP2P && conn.statusICE.Get() == StatusConnected +func (conn *Conn) isICEActive() bool { + return (conn.currentConnPriority == connPriorityICEP2P || conn.currentConnPriority == connPriorityICETurn) && conn.statusICE.Get() == StatusConnected } func (conn *Conn) removeWgPeer() error { From 53b9a2002fa8888136ed03fbb40e08b32a1e8740 Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Fri, 7 Mar 2025 14:06:47 +0100 Subject: [PATCH 09/30] Print out the goroutine id (#3433) The TXT logger prints out the actual go routine ID This feature depends on 'loggoroutine' build tag ```go build -tags loggoroutine``` --- formatter/formatter.go | 83 ------------------- formatter/hook/additional_empty.go | 9 ++ formatter/hook/additional_goroutine.go | 12 +++ formatter/{ => hook}/hook.go | 5 +- formatter/{ => hook}/hook_test.go | 2 +- formatter/hook/keys.go | 6 ++ formatter/levels/levels.go | 3 + formatter/{ => logcat}/logcat.go | 18 ++-- formatter/{ => logcat}/logcat_test.go | 3 +- formatter/set.go | 20 +++-- formatter/syslog/formatter.go | 39 +++++++++ formatter/syslog/formatter_test.go | 26 ++++++ formatter/txt/format.go | 31 +++++++ formatter/txt/format_gorutines.go | 35 ++++++++ formatter/txt/formatter.go | 31 +++++++ formatter/{ => txt}/formatter_test.go | 19 +---- go.mod | 1 + go.sum | 2 + management/cmd/management.go | 10 +-- management/cmd/migration_up.go | 4 +- management/server/management_proto_test.go | 4 +- .../server/telemetry/http_api_metrics.go | 4 +- 22 files changed, 236 insertions(+), 131 deletions(-) delete mode 100644 formatter/formatter.go create mode 100644 formatter/hook/additional_empty.go create mode 100644 formatter/hook/additional_goroutine.go rename formatter/{ => hook}/hook.go (96%) rename formatter/{ => hook}/hook_test.go (98%) create mode 100644 formatter/hook/keys.go create mode 100644 formatter/levels/levels.go rename formatter/{ => logcat}/logcat.go (63%) rename formatter/{ => logcat}/logcat_test.go (97%) create mode 100644 formatter/syslog/formatter.go create mode 100644 formatter/syslog/formatter_test.go create mode 100644 formatter/txt/format.go create mode 100644 formatter/txt/format_gorutines.go create mode 100644 formatter/txt/formatter.go rename formatter/{ => txt}/formatter_test.go (55%) diff --git a/formatter/formatter.go b/formatter/formatter.go deleted file mode 100644 index 74de38603..000000000 --- a/formatter/formatter.go +++ /dev/null @@ -1,83 +0,0 @@ -package formatter - -import ( - "fmt" - "strings" - "time" - - "github.com/sirupsen/logrus" -) - -// TextFormatter formats logs into text with included source code's path -type TextFormatter struct { - timestampFormat string - levelDesc []string -} - -// SyslogFormatter formats logs into text -type SyslogFormatter struct { - levelDesc []string -} - -var validLevelDesc = []string{"PANC", "FATL", "ERRO", "WARN", "INFO", "DEBG", "TRAC"} - - -// NewTextFormatter create new MyTextFormatter instance -func NewTextFormatter() *TextFormatter { - return &TextFormatter{ - levelDesc: validLevelDesc, - timestampFormat: time.RFC3339, // or RFC3339 - } -} - -// NewSyslogFormatter create new MySyslogFormatter instance -func NewSyslogFormatter() *SyslogFormatter { - return &SyslogFormatter{ - levelDesc: validLevelDesc, - } -} - -// Format renders a single log entry -func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) { - var fields string - keys := make([]string, 0, len(entry.Data)) - for k, v := range entry.Data { - if k == "source" { - continue - } - keys = append(keys, fmt.Sprintf("%s: %v", k, v)) - } - - if len(keys) > 0 { - fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", ")) - } - - level := f.parseLevel(entry.Level) - - return []byte(fmt.Sprintf("%s %s %s%s: %s\n", entry.Time.Format(f.timestampFormat), level, fields, entry.Data["source"], entry.Message)), nil -} - -func (f *TextFormatter) parseLevel(level logrus.Level) string { - if len(f.levelDesc) < int(level) { - return "" - } - - return f.levelDesc[level] -} - -// Format renders a single log entry -func (f *SyslogFormatter) Format(entry *logrus.Entry) ([]byte, error) { - var fields string - keys := make([]string, 0, len(entry.Data)) - for k, v := range entry.Data { - if k == "source" { - continue - } - keys = append(keys, fmt.Sprintf("%s: %v", k, v)) - } - - if len(keys) > 0 { - fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", ")) - } - return []byte(fmt.Sprintf("%s%s\n", fields, entry.Message)), nil -} diff --git a/formatter/hook/additional_empty.go b/formatter/hook/additional_empty.go new file mode 100644 index 000000000..4f5069482 --- /dev/null +++ b/formatter/hook/additional_empty.go @@ -0,0 +1,9 @@ +//go:build !loggoroutine + +package hook + +import log "github.com/sirupsen/logrus" + +func additionalEntries(_ *log.Entry) { + // This function is empty and is used to demonstrate the use of additional hooks. +} diff --git a/formatter/hook/additional_goroutine.go b/formatter/hook/additional_goroutine.go new file mode 100644 index 000000000..fb4e09f47 --- /dev/null +++ b/formatter/hook/additional_goroutine.go @@ -0,0 +1,12 @@ +//go:build loggoroutine + +package hook + +import ( + "github.com/petermattis/goid" + log "github.com/sirupsen/logrus" +) + +func additionalEntries(entry *log.Entry) { + entry.Data[EntryKeyGoroutineID] = goid.Get() +} diff --git a/formatter/hook.go b/formatter/hook/hook.go similarity index 96% rename from formatter/hook.go rename to formatter/hook/hook.go index 12f27e67d..1b6ceccc9 100644 --- a/formatter/hook.go +++ b/formatter/hook/hook.go @@ -1,4 +1,4 @@ -package formatter +package hook import ( "fmt" @@ -41,7 +41,8 @@ func (hook ContextHook) Levels() []logrus.Level { // Fire extend with the source information the entry.Data func (hook ContextHook) Fire(entry *logrus.Entry) error { src := hook.parseSrc(entry.Caller.File) - entry.Data["source"] = fmt.Sprintf("%s:%v", src, entry.Caller.Line) + entry.Data[EntryKeySource] = fmt.Sprintf("%s:%v", src, entry.Caller.Line) + additionalEntries(entry) if entry.Context == nil { return nil diff --git a/formatter/hook_test.go b/formatter/hook/hook_test.go similarity index 98% rename from formatter/hook_test.go rename to formatter/hook/hook_test.go index a4bcb0284..802163244 100644 --- a/formatter/hook_test.go +++ b/formatter/hook/hook_test.go @@ -1,4 +1,4 @@ -package formatter +package hook import ( "testing" diff --git a/formatter/hook/keys.go b/formatter/hook/keys.go new file mode 100644 index 000000000..09781a88b --- /dev/null +++ b/formatter/hook/keys.go @@ -0,0 +1,6 @@ +package hook + +const ( + EntryKeySource = "source" + EntryKeyGoroutineID = "goroutine_id" +) diff --git a/formatter/levels/levels.go b/formatter/levels/levels.go new file mode 100644 index 000000000..41ae80db3 --- /dev/null +++ b/formatter/levels/levels.go @@ -0,0 +1,3 @@ +package levels + +var ValidLevelDesc = []string{"PANC", "FATL", "ERRO", "WARN", "INFO", "DEBG", "TRAC"} diff --git a/formatter/logcat.go b/formatter/logcat/logcat.go similarity index 63% rename from formatter/logcat.go rename to formatter/logcat/logcat.go index e8f606229..c561d3283 100644 --- a/formatter/logcat.go +++ b/formatter/logcat/logcat.go @@ -1,26 +1,28 @@ -package formatter +package logcat import ( "fmt" "strings" "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/formatter/levels" ) -// LogcatFormatter formats logs into text what is fit for logcat -type LogcatFormatter struct { +// Formatter formats logs into text what is fit for logcat +type Formatter struct { levelDesc []string } // NewLogcatFormatter create new LogcatFormatter instance -func NewLogcatFormatter() *LogcatFormatter { - return &LogcatFormatter{ - levelDesc: []string{"PANC", "FATL", "ERRO", "WARN", "INFO", "DEBG", "TRAC"}, +func NewLogcatFormatter() *Formatter { + return &Formatter{ + levelDesc: levels.ValidLevelDesc, } } // Format renders a single log entry -func (f *LogcatFormatter) Format(entry *logrus.Entry) ([]byte, error) { +func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) { var fields string keys := make([]string, 0, len(entry.Data)) for k, v := range entry.Data { @@ -39,7 +41,7 @@ func (f *LogcatFormatter) Format(entry *logrus.Entry) ([]byte, error) { return []byte(fmt.Sprintf("[%s] %s%s %s\n", level, fields, entry.Data["source"], entry.Message)), nil } -func (f *LogcatFormatter) parseLevel(level logrus.Level) string { +func (f *Formatter) parseLevel(level logrus.Level) string { if len(f.levelDesc) < int(level) { return "" } diff --git a/formatter/logcat_test.go b/formatter/logcat/logcat_test.go similarity index 97% rename from formatter/logcat_test.go rename to formatter/logcat/logcat_test.go index 45ba5bc46..fd4d92881 100644 --- a/formatter/logcat_test.go +++ b/formatter/logcat/logcat_test.go @@ -1,4 +1,4 @@ -package formatter +package logcat import ( "testing" @@ -25,4 +25,5 @@ func TestLogcatMessageFormat(t *testing.T) { if parsedString != expectedString && parsedString != expectedStringVariant { t.Errorf("The log messages don't match. Expected: '%s', got: '%s'", expectedString, parsedString) } + } diff --git a/formatter/set.go b/formatter/set.go index 9dfea5a7f..a609e7b48 100644 --- a/formatter/set.go +++ b/formatter/set.go @@ -2,31 +2,37 @@ package formatter import ( "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/formatter/hook" + "github.com/netbirdio/netbird/formatter/logcat" + "github.com/netbirdio/netbird/formatter/syslog" + "github.com/netbirdio/netbird/formatter/txt" ) // SetTextFormatter set the text formatter for given logger. func SetTextFormatter(logger *logrus.Logger) { - logger.Formatter = NewTextFormatter() + logger.Formatter = txt.NewTextFormatter() logger.ReportCaller = true - logger.AddHook(NewContextHook()) + logger.AddHook(hook.NewContextHook()) } + // SetSyslogFormatter set the text formatter for given logger. func SetSyslogFormatter(logger *logrus.Logger) { - logger.Formatter = NewSyslogFormatter() + logger.Formatter = syslog.NewSyslogFormatter() logger.ReportCaller = true - logger.AddHook(NewContextHook()) + logger.AddHook(hook.NewContextHook()) } // SetJSONFormatter set the JSON formatter for given logger. func SetJSONFormatter(logger *logrus.Logger) { logger.Formatter = &logrus.JSONFormatter{} logger.ReportCaller = true - logger.AddHook(NewContextHook()) + logger.AddHook(hook.NewContextHook()) } // SetLogcatFormatter set the logcat formatter for given logger. func SetLogcatFormatter(logger *logrus.Logger) { - logger.Formatter = NewLogcatFormatter() + logger.Formatter = logcat.NewLogcatFormatter() logger.ReportCaller = true - logger.AddHook(NewContextHook()) + logger.AddHook(hook.NewContextHook()) } diff --git a/formatter/syslog/formatter.go b/formatter/syslog/formatter.go new file mode 100644 index 000000000..e72c30347 --- /dev/null +++ b/formatter/syslog/formatter.go @@ -0,0 +1,39 @@ +package syslog + +import ( + "fmt" + "strings" + + "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/formatter/levels" +) + +// Formatter formats logs into text +type Formatter struct { + levelDesc []string +} + +// NewSyslogFormatter create new MySyslogFormatter instance +func NewSyslogFormatter() *Formatter { + return &Formatter{ + levelDesc: levels.ValidLevelDesc, + } +} + +// Format renders a single log entry +func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) { + var fields string + keys := make([]string, 0, len(entry.Data)) + for k, v := range entry.Data { + if k == "source" { + continue + } + keys = append(keys, fmt.Sprintf("%s: %v", k, v)) + } + + if len(keys) > 0 { + fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", ")) + } + return []byte(fmt.Sprintf("%s%s\n", fields, entry.Message)), nil +} diff --git a/formatter/syslog/formatter_test.go b/formatter/syslog/formatter_test.go new file mode 100644 index 000000000..110a3390b --- /dev/null +++ b/formatter/syslog/formatter_test.go @@ -0,0 +1,26 @@ +package syslog + +import ( + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestLogSyslogFormat(t *testing.T) { + + someEntry := &logrus.Entry{ + Data: logrus.Fields{"att1": 1, "att2": 2, "source": "some/fancy/path.go:46"}, + Time: time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC), + Level: 3, + Message: "Some Message", + } + + formatter := NewSyslogFormatter() + result, _ := formatter.Format(someEntry) + + parsedString := string(result) + expectedString := "^\\[(att1: 1, att2: 2|att2: 2, att1: 1)\\] Some Message\\s+$" + assert.Regexp(t, expectedString, parsedString) +} diff --git a/formatter/txt/format.go b/formatter/txt/format.go new file mode 100644 index 000000000..a88c41044 --- /dev/null +++ b/formatter/txt/format.go @@ -0,0 +1,31 @@ +//go:build !loggoroutine + +package txt + +import ( + "fmt" + "strings" + + "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/formatter/hook" +) + +func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) { + var fields string + keys := make([]string, 0, len(entry.Data)) + for k, v := range entry.Data { + if k == hook.EntryKeySource { + continue + } + keys = append(keys, fmt.Sprintf("%s: %v", k, v)) + } + + if len(keys) > 0 { + fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", ")) + } + + level := f.parseLevel(entry.Level) + + return []byte(fmt.Sprintf("%s %s %s%s: %s\n", entry.Time.Format(f.timestampFormat), level, fields, entry.Data[hook.EntryKeySource], entry.Message)), nil +} diff --git a/formatter/txt/format_gorutines.go b/formatter/txt/format_gorutines.go new file mode 100644 index 000000000..a39aee633 --- /dev/null +++ b/formatter/txt/format_gorutines.go @@ -0,0 +1,35 @@ +//go:build loggoroutine + +package txt + +import ( + "fmt" + "strings" + + "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/formatter/hook" +) + +func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) { + var fields string + keys := make([]string, 0, len(entry.Data)) + for k, v := range entry.Data { + if k == hook.EntryKeySource { + continue + } + + if k == hook.EntryKeyGoroutineID { + continue + } + keys = append(keys, fmt.Sprintf("%s: %v", k, v)) + } + + if len(keys) > 0 { + fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", ")) + } + + level := f.parseLevel(entry.Level) + + return []byte(fmt.Sprintf("%s %s %d %s%s: %s\n", entry.Time.Format(f.timestampFormat), level, entry.Data[hook.EntryKeyGoroutineID], fields, entry.Data[hook.EntryKeySource], entry.Message)), nil +} diff --git a/formatter/txt/formatter.go b/formatter/txt/formatter.go new file mode 100644 index 000000000..3b2a3fb4d --- /dev/null +++ b/formatter/txt/formatter.go @@ -0,0 +1,31 @@ +package txt + +import ( + "time" + + "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/formatter/levels" +) + +// TextFormatter formats logs into text with included source code's path +type TextFormatter struct { + timestampFormat string + levelDesc []string +} + +// NewTextFormatter create new MyTextFormatter instance +func NewTextFormatter() *TextFormatter { + return &TextFormatter{ + levelDesc: levels.ValidLevelDesc, + timestampFormat: time.RFC3339, // or RFC3339 + } +} + +func (f *TextFormatter) parseLevel(level logrus.Level) string { + if len(f.levelDesc) < int(level) { + return "" + } + + return f.levelDesc[level] +} diff --git a/formatter/formatter_test.go b/formatter/txt/formatter_test.go similarity index 55% rename from formatter/formatter_test.go rename to formatter/txt/formatter_test.go index 1ed207958..590af5d50 100644 --- a/formatter/formatter_test.go +++ b/formatter/txt/formatter_test.go @@ -1,4 +1,4 @@ -package formatter +package txt import ( "testing" @@ -24,20 +24,3 @@ func TestLogTextFormat(t *testing.T) { expectedString := "^2021-02-21T01:10:30Z WARN \\[(att1: 1, att2: 2|att2: 2, att1: 1)\\] some/fancy/path.go:46: Some Message\\s+$" assert.Regexp(t, expectedString, parsedString) } - -func TestLogSyslogFormat(t *testing.T) { - - someEntry := &logrus.Entry{ - Data: logrus.Fields{"att1": 1, "att2": 2, "source": "some/fancy/path.go:46"}, - Time: time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC), - Level: 3, - Message: "Some Message", - } - - formatter := NewSyslogFormatter() - result, _ := formatter.Format(someEntry) - - parsedString := string(result) - expectedString := "^\\[(att1: 1, att2: 2|att2: 2, att1: 1)\\] Some Message\\s+$" - assert.Regexp(t, expectedString, parsedString) -} diff --git a/go.mod b/go.mod index 3d71e8eb1..76605e155 100644 --- a/go.mod +++ b/go.mod @@ -65,6 +65,7 @@ require ( github.com/okta/okta-sdk-golang/v2 v2.18.0 github.com/oschwald/maxminddb-golang v1.12.0 github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/petermattis/goid v0.0.0-20250303134427-723919f7f203 github.com/pion/logging v0.2.2 github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 diff --git a/go.sum b/go.sum index 36bca22d3..069b378d6 100644 --- a/go.sum +++ b/go.sum @@ -571,6 +571,8 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/petermattis/goid v0.0.0-20250303134427-723919f7f203 h1:E7Kmf11E4K7B5hDti2K2NqPb1nlYlGYsu02S1JNd/Bs= +github.com/petermattis/goid v0.0.0-20250303134427-723919f7f203/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= diff --git a/management/cmd/management.go b/management/cmd/management.go index 9712f04aa..e087a3d67 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -36,7 +36,7 @@ import ( "github.com/netbirdio/management-integrations/integrations" "github.com/netbirdio/netbird/encryption" - "github.com/netbirdio/netbird/formatter" + "github.com/netbirdio/netbird/formatter/hook" mgmtProto "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/auth" @@ -90,7 +90,7 @@ var ( flag.Parse() //nolint - ctx := context.WithValue(cmd.Context(), formatter.ExecutionContextKey, formatter.SystemSource) + ctx := context.WithValue(cmd.Context(), hook.ExecutionContextKey, hook.SystemSource) err := util.InitLog(logLevel, logFile) if err != nil { @@ -136,7 +136,7 @@ var ( ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() //nolint - ctx = context.WithValue(ctx, formatter.ExecutionContextKey, formatter.SystemSource) + ctx = context.WithValue(ctx, hook.ExecutionContextKey, hook.SystemSource) err := handleRebrand(cmd) if err != nil { @@ -374,7 +374,7 @@ func unaryInterceptor( ) (interface{}, error) { reqID := uuid.New().String() //nolint - ctx = context.WithValue(ctx, formatter.ExecutionContextKey, formatter.GRPCSource) + ctx = context.WithValue(ctx, hook.ExecutionContextKey, hook.GRPCSource) //nolint ctx = context.WithValue(ctx, nbContext.RequestIDKey, reqID) return handler(ctx, req) @@ -389,7 +389,7 @@ func streamInterceptor( reqID := uuid.New().String() wrapped := grpcMiddleware.WrapServerStream(ss) //nolint - ctx := context.WithValue(ss.Context(), formatter.ExecutionContextKey, formatter.GRPCSource) + ctx := context.WithValue(ss.Context(), hook.ExecutionContextKey, hook.GRPCSource) //nolint wrapped.WrappedContext = context.WithValue(ctx, nbContext.RequestIDKey, reqID) return handler(srv, wrapped) diff --git a/management/cmd/migration_up.go b/management/cmd/migration_up.go index 183fc554d..de061dca2 100644 --- a/management/cmd/migration_up.go +++ b/management/cmd/migration_up.go @@ -8,7 +8,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/netbirdio/netbird/formatter" + "github.com/netbirdio/netbird/formatter/hook" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/util" ) @@ -30,7 +30,7 @@ var upCmd = &cobra.Command{ } //nolint - ctx := context.WithValue(cmd.Context(), formatter.ExecutionContextKey, formatter.SystemSource) + ctx := context.WithValue(cmd.Context(), hook.ExecutionContextKey, hook.SystemSource) if err := store.MigrateFileStoreToSqlite(ctx, mgmtDataDir); err != nil { return err diff --git a/management/server/management_proto_test.go b/management/server/management_proto_test.go index 4d0630f0f..28f5abf57 100644 --- a/management/server/management_proto_test.go +++ b/management/server/management_proto_test.go @@ -20,7 +20,7 @@ import ( "google.golang.org/grpc/keepalive" "github.com/netbirdio/netbird/encryption" - "github.com/netbirdio/netbird/formatter" + "github.com/netbirdio/netbird/formatter/hook" mgmtProto "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/settings" @@ -424,7 +424,7 @@ func startManagementForTest(t *testing.T, testFile string, config *Config) (*grp peersUpdateManager := NewPeersUpdateManager(nil) eventStore := &activity.InMemoryEventStore{} - ctx := context.WithValue(context.Background(), formatter.ExecutionContextKey, formatter.SystemSource) //nolint:staticcheck + ctx := context.WithValue(context.Background(), hook.ExecutionContextKey, hook.SystemSource) //nolint:staticcheck metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) require.NoError(t, err) diff --git a/management/server/telemetry/http_api_metrics.go b/management/server/telemetry/http_api_metrics.go index 5ef9e6d02..ae27466d9 100644 --- a/management/server/telemetry/http_api_metrics.go +++ b/management/server/telemetry/http_api_metrics.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" - "github.com/netbirdio/netbird/formatter" + "github.com/netbirdio/netbird/formatter/hook" nbContext "github.com/netbirdio/netbird/management/server/context" ) @@ -167,7 +167,7 @@ func (m *HTTPMiddleware) Handler(h http.Handler) http.Handler { reqStart := time.Now() //nolint - ctx := context.WithValue(r.Context(), formatter.ExecutionContextKey, formatter.HTTPSource) + ctx := context.WithValue(r.Context(), hook.ExecutionContextKey, hook.HTTPSource) reqID := uuid.New().String() //nolint From a444e551b3b356109b50ad3fb6c7e3ccb9ebc44b Mon Sep 17 00:00:00 2001 From: Philippe Vaucher Date: Fri, 7 Mar 2025 16:10:11 +0100 Subject: [PATCH 10/30] [misc] Traefik config improvements (#3346) * Remove deprecated docker-compose version * Prettify docker-compose files * Backports missing logging entries * Fix signal port * Add missing relay configuration * Serve management over 33073 to avoid confusion --- infrastructure_files/docker-compose.yml.tmpl | 6 ++- .../docker-compose.yml.tmpl.traefik | 42 +++++++++++++++---- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/infrastructure_files/docker-compose.yml.tmpl b/infrastructure_files/docker-compose.yml.tmpl index b7904fb5b..dc491ae23 100644 --- a/infrastructure_files/docker-compose.yml.tmpl +++ b/infrastructure_files/docker-compose.yml.tmpl @@ -1,6 +1,5 @@ -version: "3" services: - #UI dashboard + # UI dashboard dashboard: image: netbirdio/dashboard:$NETBIRD_DASHBOARD_TAG restart: unless-stopped @@ -33,6 +32,7 @@ services: options: max-size: "500m" max-file: "2" + # Signal signal: image: netbirdio/signal:$NETBIRD_SIGNAL_TAG @@ -49,6 +49,7 @@ services: options: max-size: "500m" max-file: "2" + # Relay relay: image: netbirdio/relay:$NETBIRD_RELAY_TAG @@ -115,6 +116,7 @@ services: options: max-size: "500m" max-file: "2" + volumes: $MGMT_VOLUMENAME: $SIGNAL_VOLUMENAME: diff --git a/infrastructure_files/docker-compose.yml.tmpl.traefik b/infrastructure_files/docker-compose.yml.tmpl.traefik index dcd3f955c..b62d15b7c 100644 --- a/infrastructure_files/docker-compose.yml.tmpl.traefik +++ b/infrastructure_files/docker-compose.yml.tmpl.traefik @@ -1,6 +1,5 @@ -version: "3" services: - #UI dashboard + # UI dashboard dashboard: image: netbirdio/dashboard:$NETBIRD_DASHBOARD_TAG restart: unless-stopped @@ -32,6 +31,11 @@ services: - traefik.enable=true - traefik.http.routers.netbird-dashboard.rule=Host(`$NETBIRD_DOMAIN`) - traefik.http.services.netbird-dashboard.loadbalancer.server.port=80 + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" # Signal signal: @@ -40,15 +44,20 @@ services: volumes: - $SIGNAL_VOLUMENAME:/var/lib/netbird #ports: - # - 10000:80 + # - $NETBIRD_SIGNAL_PORT:80 # # port and command for Let's Encrypt validation # - 443:443 # command: ["--letsencrypt-domain", "$NETBIRD_LETSENCRYPT_DOMAIN", "--log-file", "console"] labels: - traefik.enable=true - traefik.http.routers.netbird-signal.rule=Host(`$NETBIRD_DOMAIN`) && PathPrefix(`/signalexchange.SignalExchange/`) - - traefik.http.services.netbird-signal.loadbalancer.server.port=80 + - traefik.http.services.netbird-signal.loadbalancer.server.port=10000 - traefik.http.services.netbird-signal.loadbalancer.server.scheme=h2c + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" # Relay relay: @@ -60,8 +69,12 @@ services: - NB_EXPOSED_ADDRESS=$NETBIRD_RELAY_DOMAIN:$NETBIRD_RELAY_PORT # todo: change to a secure secret - NB_AUTH_SECRET=$NETBIRD_RELAY_AUTH_SECRET - ports: - - $NETBIRD_RELAY_PORT:$NETBIRD_RELAY_PORT + # ports: + # - $NETBIRD_RELAY_PORT:$NETBIRD_RELAY_PORT + labels: + - traefik.enable=true + - traefik.http.routers.netbird-relay.rule=Host(`$NETBIRD_DOMAIN`) && PathPrefix(`/relay`) + - traefik.http.services.netbird-relay.loadbalancer.server.port=33080 logging: driver: "json-file" options: @@ -87,8 +100,9 @@ services: # # command for Let's Encrypt validation without dashboard container # command: ["--letsencrypt-domain", "$NETBIRD_LETSENCRYPT_DOMAIN", "--log-file", "console"] command: [ - "--port", "443", + "--port", "33073", "--log-file", "console", + "--log-level", "info", "--disable-anonymous-metrics=$NETBIRD_DISABLE_ANONYMOUS_METRICS", "--single-account-mode-domain=$NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN", "--dns-domain=$NETBIRD_MGMT_DNS_DOMAIN" @@ -97,12 +111,17 @@ services: - traefik.enable=true - traefik.http.routers.netbird-api.rule=Host(`$NETBIRD_DOMAIN`) && PathPrefix(`/api`) - traefik.http.routers.netbird-api.service=netbird-api - - traefik.http.services.netbird-api.loadbalancer.server.port=443 + - traefik.http.services.netbird-api.loadbalancer.server.port=33073 - traefik.http.routers.netbird-management.rule=Host(`$NETBIRD_DOMAIN`) && PathPrefix(`/management.ManagementService/`) - traefik.http.routers.netbird-management.service=netbird-management - - traefik.http.services.netbird-management.loadbalancer.server.port=443 + - traefik.http.services.netbird-management.loadbalancer.server.port=33073 - traefik.http.services.netbird-management.loadbalancer.server.scheme=h2c + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" environment: - NETBIRD_STORE_ENGINE_POSTGRES_DSN=$NETBIRD_STORE_ENGINE_POSTGRES_DSN - NETBIRD_STORE_ENGINE_MYSQL_DSN=$NETBIRD_STORE_ENGINE_MYSQL_DSN @@ -119,6 +138,11 @@ services: network_mode: host command: - -c /etc/turnserver.conf + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" volumes: $MGMT_VOLUMENAME: From ae6b61301c08dcf93eeec553a38c96acd7efa515 Mon Sep 17 00:00:00 2001 From: Muzammil Date: Fri, 7 Mar 2025 20:13:11 +0500 Subject: [PATCH 11/30] Muz/netbird dashboards (#3458) * added all 3 dashboards * update readme --- .../grafana/dashboards/.gitignore | 2 + .../grafana/dashboards/management.json | 3569 +++++++++++++++++ .../grafana/dashboards/readme.md | 12 + .../grafana/dashboards/relay.json | 926 +++++ .../grafana/dashboards/signal.json | 1877 +++++++++ 5 files changed, 6386 insertions(+) create mode 100644 infrastructure_files/observability/grafana/dashboards/.gitignore create mode 100644 infrastructure_files/observability/grafana/dashboards/management.json create mode 100644 infrastructure_files/observability/grafana/dashboards/readme.md create mode 100644 infrastructure_files/observability/grafana/dashboards/relay.json create mode 100644 infrastructure_files/observability/grafana/dashboards/signal.json diff --git a/infrastructure_files/observability/grafana/dashboards/.gitignore b/infrastructure_files/observability/grafana/dashboards/.gitignore new file mode 100644 index 000000000..e89fdc9ac --- /dev/null +++ b/infrastructure_files/observability/grafana/dashboards/.gitignore @@ -0,0 +1,2 @@ +# Some files eg. management.json are being ignored by root .gitignore. Need to un-ignore all json dashboards here. +!*.json \ No newline at end of file diff --git a/infrastructure_files/observability/grafana/dashboards/management.json b/infrastructure_files/observability/grafana/dashboards/management.json new file mode 100644 index 000000000..95983603f --- /dev/null +++ b/infrastructure_files/observability/grafana/dashboards/management.json @@ -0,0 +1,3569 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 82, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of active peer streams connected to the gRPC server", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + }, + { + "color": "red", + "value": 0 + }, + { + "color": "#EAB839", + "value": 8000 + }, + { + "color": "green", + "value": 11000 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 14, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": true, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "management_grpc_connected_streams_ratio{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "Connected grpc streams", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Connected peers", + "type": "stat" + }, + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 59, + "title": "Core metrics / Peers", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of active peer streams connected to the gRPC server over time", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 60, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "management_grpc_connected_streams_ratio{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}", + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}/{{host}}", + "range": true, + "refId": "A" + } + ], + "title": "Connected peers count", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Rate of updates with new meta data from the peer.\n\nConstant high numbers of meta updates indicate a version with an issue. This can generate an issue on larger deployments since it will trigger an account-wide network map update.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 61, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "rate(management_account_peer_meta_update_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])", + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}", + "range": true, + "refId": "A" + } + ], + "title": "Peer metadata updates rate", + "type": "timeseries" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 34, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of calculating the peer network map that is sent to the clients.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(management_account_get_peer_network_map_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.9,sum(increase(management_account_get_peer_network_map_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(management_account_get_peer_network_map_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "GetPeerNetworkMap Latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of triggering the account peers update and preparing the required data for the network map being sent to the clients.\n", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 40, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(management_account_update_account_peers_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.9,sum(increase(management_account_update_account_peers_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(management_account_update_account_peers_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "UpdateAccountPeers Latency", + "type": "timeseries" + } + ], + "title": "Core metrics / Network map", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 39, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of update messages piling up in the update channel queue.\n\nA high queue number indicates either a network issue or a high number of account updates.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 56, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(management_grpc_updatechannel_queue_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.9,sum(increase(management_grpc_updatechannel_queue_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(management_grpc_updatechannel_queue_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "updatechannel queue length", + "type": "timeseries" + } + ], + "title": "Core metrics / updatechannel", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 15 + }, + "id": 38, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of how long it takes to save or delete an account in the store\n\nWill let you know if the DB is not keeping up with the requests coming from management", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 41, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(management_store_persistence_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(management_store_persistence_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(management_store_persistence_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Database persistence latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of how long it takes to execute a transaction in the store\n\nWill let you know if the DB is not keeping up with the requests coming from management", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 42, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(management_store_transaction_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(management_store_transaction_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(management_store_transaction_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Database transaction latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of how long a process holds the acquired global lock in the store", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 58, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(management_store_global_lock_acquisition_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(management_store_global_lock_acquisition_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(management_store_global_lock_acquisition_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Global lock duration", + "type": "timeseries" + } + ], + "title": "Core metrics / store persistence", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 37, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "A high number for a long period might indicate an inconsistency between the number of local users and IdP", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 47, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "rate(management_idp_authenticate_request_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])", + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}", + "range": true, + "refId": "A" + } + ], + "title": "IdP authenticate request rate ", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "A high number for a long period might indicate an inconsistency between the number of local users and IdP", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 48, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "rate(management_idp_get_account_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])", + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}", + "range": true, + "refId": "A" + } + ], + "title": "IdP get_account request rate ", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqpm" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "id": 49, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "rate(management_idp_update_user_meta_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])", + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}", + "range": true, + "refId": "A" + } + ], + "title": "IdP update_user_meta request rate ", + "type": "timeseries" + } + ], + "title": "Core metrics / IdP", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 36, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 50, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(management_http_request_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",method=~\"GET|OPTIONS\"}[$__rate_interval])) by (job,method)", + "instant": false, + "legendFormat": "{{method}}", + "range": true, + "refId": "A" + } + ], + "title": "Read request rate (GET, OPTIONS)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 51, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(management_http_request_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",method=~\"POST|PUT|DELETE\"}[$__rate_interval])) by (job,method)", + "instant": false, + "legendFormat": "{{method}}", + "range": true, + "refId": "A" + } + ], + "title": "Write request rate (PUT, POST,DELETE)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of incoming read HTTP requests (GET, OPTIONS)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 26 + }, + "id": 52, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(management_http_request_duration_ms_total_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",type=~\"read\"}[5m])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(management_http_request_duration_ms_total_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",type=~\"read\"}[5m])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(management_http_request_duration_ms_total_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",type=~\"read\"}[5m])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Read request latency (GET, OPTIONS)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of incoming write HTTP requests (PUT, POST,DELETE)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 26 + }, + "id": 53, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(management_http_request_duration_ms_total_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",type=~\"write\"}[5m])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(management_http_request_duration_ms_total_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",type=~\"write\"}[5m])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(management_http_request_duration_ms_total_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",type=~\"write\"}[5m])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Write request latency (PUT, POST,DELETE)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 34 + }, + "id": 54, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(management_http_request_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (job,exported_endpoint,method)", + "hide": false, + "instant": false, + "legendFormat": "{{method}}-{{exported_endpoint}}", + "range": true, + "refId": "A" + } + ], + "title": "Request rate by endpoint", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 41 + }, + "id": 55, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(management_http_request_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[5m])) by (exported_endpoint,method,le))", + "hide": false, + "instant": false, + "legendFormat": "{{method}}-{{exported_endpoint}}", + "range": true, + "refId": "B" + } + ], + "title": "p90 latency by endpoint", + "type": "timeseries" + } + ], + "title": "HTTP metrics", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 35, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Rate of sync gRPC requests from the peers to establish a connection and receive network map updates (update channel)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 43, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(management_grpc_sync_request_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (job)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Sync request rate ", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of the sync gRPC requests from the peers to establish a connection and receive network map updates (update channel)\n\nIf those are to high client will probably retry before management is able to respond\n", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 45, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(management_grpc_sync_request_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(management_grpc_sync_request_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(management_grpc_sync_request_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Sync request latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Rate of login gRPC requests from the peers to authenticate and receive initial configuration and relay credentials", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 26 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(management_grpc_login_request_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (job)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Login request rate ", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of the login gRPC requests from the peers to authenticate and receive initial configuration and relay credentials\n\nIf those are to high client will probably retry before management is able to respond\n", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 26 + }, + "id": 46, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(management_grpc_login_request_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(management_grpc_login_request_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(management_grpc_login_request_duration_ms_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Login request latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "GetKey gRPC request rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 34 + }, + "id": 57, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(management_grpc_key_request_counter_ratio_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (job)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "GetKey request rate ", + "type": "timeseries" + } + ], + "title": "RPC metrics", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 21, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 69, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 51 + }, + "id": 11, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Mean", + "sortDesc": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "normal", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 100 + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "delta(management_updatechannel_close_one_duration_micro_count{application=\"management\", environment=\"$environment\", host=~\"$host\"}[$interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "CloseOne", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum(delta(management_updatechannel_send_duration_micro_count{host=\"$host\", environment=\"$environment\"}[$interval]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "Send", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum(delta(management_updatechannel_create_duration_micro_count{host=\"$host\", environment=\"$environment\"}[$interval]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "Create", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "delta(management_updatechannel_get_all_duration_micro_count{host=\"$host\", environment=\"$environment\"}[$interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "GetAll", + "range": true, + "refId": "D", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "delta(management_updatechannel_haschannel_duration_micro_count{host=\"$host\", environment=\"$environment\"}[$interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "HasChannel", + "range": true, + "refId": "E", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "delta(management_updatechannel_close_multiple_channels_count{host=\"$host\", environment=\"$environment\"}[$interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "CloseMultiple", + "range": true, + "refId": "F", + "useBackend": false + } + ], + "title": "Update Channel operations", + "type": "barchart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of update channel operations by operation", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "µs" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 51 + }, + "id": 13, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (rate(management_updatechannel_close_one_duration_micro_bucket{application=\"management\", environment=\"$environment\", host=~\"$host\"}[$interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "CloseOne", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by(le) (rate(management_updatechannel_send_duration_micro_bucket{host=\"$host\", environment=\"$environment\"}[$interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "Send", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by(le) (rate(management_updatechannel_create_duration_micro_bucket{host=\"$host\", environment=\"$environment\"}[$interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "Create", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by(le) (rate(management_updatechannel_get_all_duration_micro_bucket{host=\"$host\", environment=\"$environment\"}[$interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "GetAll", + "range": true, + "refId": "D", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by(le) (rate(management_updatechannel_haschannel_duration_micro_bucket{host=\"$host\", environment=\"$environment\"}[$interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "CloseMultiple", + "range": true, + "refId": "E", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by(le) (rate(management_updatechannel_close_multiple_channels_bucket{host=\"$host\", environment=\"$environment\"}[$interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "HasChannel", + "range": true, + "refId": "F", + "useBackend": false + } + ], + "title": "Update Channel methods p95 Duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Ratio between closed and created update channels", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 100, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 32, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "percent" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "C {__name__=\"management_updatechannel_create_duration_micro_sum\", closed=\"true\", environment=\"production\", host=\"management\", instance=\"localhost:8081\", job=\"prometheus.scrape.local\"}" + }, + "properties": [ + { + "id": "displayName", + "value": "Recreation" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 59 + }, + "id": 12, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "delta(management_updatechannel_create_duration_micro_count{environment=\"$environment\", closed=\"true\", application=\"management\", host=~\"$host\"}[$interval])", + "fullMetaSearch": false, + "hide": true, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(environment) (delta(management_updatechannel_create_duration_micro_count{environment=\"$environment\", application=\"management\"}[$interval]))", + "fullMetaSearch": false, + "hide": true, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "__expr__", + "uid": "${DS_EXPRESSION}" + }, + "expression": "100-$A/$B*100", + "hide": false, + "refId": "Rate", + "type": "math" + } + ], + "title": "Percentage of Recreated channels", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of update messages piling up in the update channel queue.\n\nA high queue number indicates either a network issue or a high number of account updates.", + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 59 + }, + "id": 27, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Greens", + "steps": 73 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "axisWidth": 60, + "decimals": 1, + "reverse": false, + "unit": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(le) (increase(management_grpc_updatechannel_queue_bucket{application=\"management\", environment=\"$environment\", host=~\"$host\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Update Channel heat map", + "type": "heatmap" + } + ], + "title": "General", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 22, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of triggering the account peers update and preparing the required data for the network map being sent to the clients.\n\nWhen you see a lighter color on a higher timing bucket, your users may experience issues.", + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 23, + "x": 0, + "y": 83 + }, + "id": 20, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "axisWidth": 60, + "decimals": 1, + "reverse": false, + "unit": "ms" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(le) (increase(management_account_update_account_peers_duration_ms_bucket{application=\"management\", environment=\"$environment\", host=~\"$host\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "UpdateAccountPeers Latency", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of objects in the network map like peers, routes, firewall rules, etc. that are sent to the clients", + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 23, + "x": 0, + "y": 91 + }, + "id": 19, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "axisWidth": 60, + "decimals": 0, + "reverse": false, + "unit": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "sum by(le) (increase(management_account_network_map_object_count_bucket{application=\"management\", environment=\"$environment\", host=~\"$host\"}[$__rate_interval]))", + "format": "heatmap", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "NetworkMap Objects", + "type": "heatmap" + } + ], + "title": "Network Map", + "type": "row" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "73c8e14b-5699-4876-b887-4299930521a5" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(management_grpc_connected_streams_ratio,cluster)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "cluster", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(management_grpc_connected_streams_ratio,cluster)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "None", + "value": "" + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(management_grpc_connected_streams_ratio{cluster=\"$cluster\"},environment)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "environment", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(management_grpc_connected_streams_ratio{cluster=\"$cluster\"},environment)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "netbird-management", + "value": "netbird-management" + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(management_grpc_connected_streams_ratio{cluster=\"$cluster\", environment=\"$environment\"},job)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "job", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(management_grpc_connected_streams_ratio{cluster=\"$cluster\", environment=\"$environment\"},job)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(management_grpc_connected_streams_ratio{cluster=\"$cluster\", environment=\"$environment\", job=\"$job\"},host)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "host", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(management_grpc_connected_streams_ratio{cluster=\"$cluster\", environment=\"$environment\", job=\"$job\"},host)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Netbird / Management", + "uid": "bebypwoziton4b-v4", + "version": 38, + "weekStart": "" +} \ No newline at end of file diff --git a/infrastructure_files/observability/grafana/dashboards/readme.md b/infrastructure_files/observability/grafana/dashboards/readme.md new file mode 100644 index 000000000..3160921a6 --- /dev/null +++ b/infrastructure_files/observability/grafana/dashboards/readme.md @@ -0,0 +1,12 @@ +## Dashboard variables + +1. **datasource**: Select Prometheus server +2. **cluster**: Filter NetBird instances by cluster +3. **environment**: Filter by environment (dev, staging, UAT, prod) +4. **job**: Select target NetBird instance if multiple are running +5. **host**: Filter metrics by host + + +NOTE: +- Your installation may have a subset of these variables. +- The dashboard expects `exported_endpoint` instead of `endpoint` in HTTP request metrics. \ No newline at end of file diff --git a/infrastructure_files/observability/grafana/dashboards/relay.json b/infrastructure_files/observability/grafana/dashboards/relay.json new file mode 100644 index 000000000..b80c59743 --- /dev/null +++ b/infrastructure_files/observability/grafana/dashboards/relay.json @@ -0,0 +1,926 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 97, + "links": [], + "panels": [ + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 24, + "title": "Core metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of connected peers by host", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 1 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "relay_peers{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Connected peers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of active connected peers by host\n\nIdeally, this number would stay around 20% of the total connections, indicating that most of the connections are P2P", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + }, + { + "color": "red", + "value": 0 + }, + { + "color": "#EAB839", + "value": 8000 + }, + { + "color": "green", + "value": 11000 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Idle peers" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#727374", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 1 + }, + "id": 16, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "relay_peers_active{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}", + "format": "time_series", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Active peers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of idle connected peers by host", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-RdYlGr" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + }, + { + "color": "red", + "value": 0 + }, + { + "color": "#EAB839", + "value": 8000 + }, + { + "color": "green", + "value": 11000 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Idle peers" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#727374", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 1 + }, + "id": 19, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "relay_peers_idle{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Idle peers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Authentication latency faced by each relay peer", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 25, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(rate(relay_peer_authentication_time_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}[$__rate_interval])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.9,sum(rate(relay_peer_authentication_time_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(rate(relay_peer_authentication_time_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Relay peer authentication latency ", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Time taken for storing each peer connection and metadata into in-memory database", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 26, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(rate(relay_peer_store_time_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}[$__rate_interval])) by (le))", + "instant": false, + "legendFormat": "p50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.9,sum(rate(relay_peer_store_time_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p90", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(rate(relay_peer_store_time_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}[$__rate_interval])) by (le))", + "hide": false, + "instant": false, + "legendFormat": "p99", + "range": true, + "refId": "C" + } + ], + "title": "Relay peer store latency ", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Total number of bytes sent/received to peers ", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 36, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 21, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(relay_transfer_sent_bytes_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "sent", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(relay_transfer_received_bytes_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}[$__rate_interval]) *-1", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "received", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Relay traffic bandwidth", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": "Prometheus", + "value": "73c8e14b-5699-4876-b887-4299930521a5" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "isNone": true, + "selected": false, + "text": "None", + "value": "" + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(relay_peers,cluster)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "cluster", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(relay_peers,cluster)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "isNone": true, + "selected": false, + "text": "None", + "value": "" + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(relay_peers{cluster=\"$cluster\"},environment)", + "description": "", + "hide": 0, + "includeAll": false, + "label": "environment", + "multi": false, + "name": "environment", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(relay_peers{cluster=\"$cluster\"},environment)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "netbird-relay", + "value": "netbird-relay" + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(relay_peers{cluster=\"$cluster\", environment=\"$environment\"},job)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "job", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(relay_peers{cluster=\"$cluster\", environment=\"$environment\"},job)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": "", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(relay_peers{cluster=\"$cluster\", environment=\"$environment\", job=\"$job\"},instance)", + "hide": 0, + "includeAll": true, + "label": "host", + "multi": true, + "name": "host", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(relay_peers{cluster=\"$cluster\", environment=\"$environment\", job=\"$job\"},instance)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Netbird / Relay", + "uid": "febyq2pgq2u-v003", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/infrastructure_files/observability/grafana/dashboards/signal.json b/infrastructure_files/observability/grafana/dashboards/signal.json new file mode 100644 index 000000000..5e36f6ce6 --- /dev/null +++ b/infrastructure_files/observability/grafana/dashboards/signal.json @@ -0,0 +1,1877 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.1.1" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": null, + "links": [], + "panels": [ + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 13, + "title": "Core metrics / peers", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of active connected peers", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": true, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum(active_peers{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\"}) by (cluster,environment,job)", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}/", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total active peers", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of how long a peer was connected\n\nThis is mostly informational, but if most of the connections are short lived, you may need to check any reverse proxy or connectivity issues", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(peer_connection_duration_seconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90,sum(increase(peer_connection_duration_seconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(peer_connection_duration_seconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "Peer connection duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Number of active connected peers by host", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "max": 25000, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 15000 + }, + { + "color": "red", + "value": 20000 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "{__name__=\"active_peers\", application=\"signal\", environment=\"prod\", host=\"signal-1-prod\", instance=\"localhost:9090\", job=\"prometheus.scrape.local\"}" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [] + } + ] + }, + "gridPos": { + "h": 4, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "active_peers{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}/{{host}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Active Peer-Connections per Host", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 14, + "panels": [], + "title": "Core metrics / messages", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "No. of messages forwarded per seconds. \nThis doesn't include the failed message count. They are tracked separately.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "mps" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "rate(messages_forwarded_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])", + "instant": false, + "legendFormat": "{{cluster}}/{{environment}}/{{job}}", + "range": true, + "refId": "A" + } + ], + "title": "Forwarded messages rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "mps" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(message_forward_failures_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (cluster,environment,job,type)", + "instant": false, + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Forwarded messages failure rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(message_forward_latency_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90,sum(increase(message_forward_latency_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(message_forward_latency_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "Forwarded messages latency", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 18, + "panels": [], + "title": "Core metrics / registerations", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "rate of peer Registration and deregistration", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "cps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 5, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(registrations_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "registration", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(deregistrations_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "legendFormat": "deregistration", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Registration and deregistration rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "mps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(registration_failures_total{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (cluster,environment,job,error)", + "instant": false, + "legendFormat": "{{error}}", + "range": true, + "refId": "A" + } + ], + "title": "Registration failure rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "time needed to register new peers", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(registration_delay_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90,sum(increase(registration_delay_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(registration_delay_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "Peer registration latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Duration of how long it takes to load a connection from the registry.\n\nNOTE: It should be below 1 ms", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 32 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(get_registration_delay_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90,sum(increase(get_registration_delay_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(get_registration_delay_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\"}[$__rate_interval])) by (le,cluster,environment,job))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "get_registration request latency", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 40 + }, + "id": 12, + "panels": [], + "title": "RPC metrics", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Send method is used by peers intending to send a message to another peer. The send covers the RPC call of sending the message from the initiator to signal.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 41 + }, + "id": 9, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(rpc_server_requests_per_rpc_count{rpc_method=~\"Send\"}[$__rate_interval])) by (rpc_method,rpc_grpc_status_code)", + "hide": false, + "instant": false, + "legendFormat": "code: {{rpc_grpc_status_code}}", + "range": true, + "refId": "B" + } + ], + "title": "RPC requests rate (method: Send)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 41 + }, + "id": 23, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81732", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "expr": "sum(rate(rpc_server_requests_per_rpc_count{rpc_method=~\"ConnectStream\"}[$__rate_interval])) by (rpc_method,rpc_grpc_status_code)", + "hide": false, + "instant": false, + "legendFormat": "code: {{rpc_grpc_status_code}}", + "range": true, + "refId": "B" + } + ], + "title": "RPC requests rate (method: ConnectStream)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 49 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(rpc_server_duration_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",rpc_method=~\"Send\"}[$__rate_interval])) by (le,cluster,environment,job,rpc_method))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90,sum(increase(rpc_server_duration_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",rpc_method=~\"Send\"}[$__rate_interval])) by (le,cluster,environment,job,rpc_method))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(rpc_server_duration_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",rpc_method=~\"Send\"}[$__rate_interval])) by (le,cluster,environment,job,rpc_method))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "RPC request latency (method: Send)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "description": "Shows estimated time of how long a peer was connected", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 49 + }, + "id": 25, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.5,sum(increase(rpc_server_duration_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",rpc_method=~\"ConnectStream\"}[$__rate_interval])) by (le,cluster,environment,job,rpc_method))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p50", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90,sum(increase(rpc_server_duration_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",rpc_method=~\"ConnectStream\"}[$__rate_interval])) by (le,cluster,environment,job,rpc_method))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p90", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99,sum(increase(rpc_server_duration_milliseconds_bucket{cluster=~\"$cluster\",environment=~\"$environment\",job=~\"$job\",host=~\"$host\",rpc_method=~\"ConnectStream\"}[$__rate_interval])) by (le,cluster,environment,job,rpc_method))", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "legendFormat": "p99", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "RPC request latency (method: ConnectStream)", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": {}, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "datasource", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(active_peers,cluster)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "cluster", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(active_peers,cluster)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(active_peers{cluster=\"$cluster\"},environment)", + "description": "", + "hide": 0, + "includeAll": false, + "label": "environment", + "multi": false, + "name": "environment", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(active_peers{cluster=\"$cluster\"},environment)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(active_peers{cluster=\"$cluster\", environment=\"$environment\"},job)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "job", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(active_peers{cluster=\"$cluster\", environment=\"$environment\"},job)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": "", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "definition": "label_values(active_peers{cluster=\"$cluster\", environment=\"$environment\", job=\"$job\"},host)", + "hide": 0, + "includeAll": true, + "label": "", + "multi": true, + "name": "host", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(active_peers{cluster=\"$cluster\", environment=\"$environment\", job=\"$job\"},host)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Netbird / Signal", + "uid": "cebyq0fs0m-v001", + "version": 15, + "weekStart": "" + } \ No newline at end of file From fc1da94520378b173cf8264df437b08d2dff0746 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Sun, 9 Mar 2025 16:06:43 +0100 Subject: [PATCH 12/30] [client, management] Add port forwarding (#3275) Add initial support to ingress ports on the client code. - new types where added - new protocol messages and controller --- .github/workflows/golang-test-linux.yml | 10 +- client/cmd/forwarding_rules.go | 98 + client/cmd/root.go | 3 + client/cmd/testutil_test.go | 3 +- client/firewall/iptables/acl_linux.go | 21 +- client/firewall/iptables/manager_linux.go | 18 +- client/firewall/iptables/router_linux.go | 259 ++- client/firewall/iptables/router_linux_test.go | 18 +- client/firewall/iptables/rule.go | 2 +- client/firewall/manager/firewall.go | 10 +- client/firewall/manager/forward_rule.go | 27 + client/firewall/manager/port.go | 41 +- client/firewall/manager/protocol.go | 19 + client/firewall/nftables/acl_linux.go | 6 +- client/firewall/nftables/manager_linux.go | 16 + client/firewall/nftables/router_linux.go | 368 +++- client/firewall/nftables/router_linux_test.go | 18 +- client/firewall/nftables/rule_linux.go | 2 +- client/firewall/uspfilter/rule.go | 8 +- client/firewall/uspfilter/uspfilter.go | 20 +- client/firewall/uspfilter/uspfilter_test.go | 4 +- client/internal/acl/id/id.go | 2 +- client/internal/acl/manager.go | 4 +- client/internal/acl/manager_test.go | 4 +- client/internal/engine.go | 104 +- client/internal/engine_test.go | 3 +- client/internal/ingressgw/manager.go | 107 ++ client/internal/ingressgw/manager_test.go | 281 +++ client/internal/message_convert.go | 58 + client/internal/peer/status.go | 58 +- client/internal/routemanager/client.go | 2 +- .../routemanager/ipfwdstate/ipfwdstate.go | 51 + .../routemanager/server_nonandroid.go | 6 +- client/proto/daemon.pb.go | 1604 +++++++++++------ client/proto/daemon.proto | 34 +- client/proto/daemon_grpc.pb.go | 36 + client/server/forwardingrules.go | 54 + client/server/server.go | 1 + client/server/server_test.go | 3 +- client/status/status.go | 64 +- client/status/status_test.go | 4 + go.mod | 60 +- go.sum | 131 +- management/client/client_test.go | 3 +- management/cmd/management.go | 18 +- management/domain/list.go | 26 +- management/domain/list_test.go | 49 + management/proto/management.pb.go | 625 ++++--- management/proto/management.proto | 16 + management/server/account.go | 9 +- management/server/account_test.go | 3 +- management/server/dns_test.go | 3 +- management/server/grpcserver.go | 8 + management/server/http/api/openapi.yml | 560 ++++++ management/server/http/api/types.gen.go | 190 ++ management/server/http/handler.go | 15 +- .../http/handlers/groups/groups_handler.go | 8 +- .../handlers/groups/groups_handler_test.go | 2 +- .../http/handlers/peers/peers_handler.go | 5 +- .../http/handlers/peers/peers_handler_test.go | 2 +- .../http/testing/testing_tools/tools.go | 12 +- management/server/integrated_validator.go | 2 +- .../integrated_validator/interface.go | 0 .../port_forwarding/controller.go | 32 + management/server/management_proto_test.go | 3 +- management/server/management_test.go | 2 + management/server/mock_server/account_mock.go | 6 +- management/server/nameserver_test.go | 3 +- management/server/peer.go | 80 +- management/server/peer_test.go | 33 +- management/server/peers/manager.go | 54 + management/server/policy.go | 1 + management/server/policy_test.go | 2 +- management/server/route_test.go | 11 +- management/server/store/sql_store.go | 14 +- management/server/store/sql_store_test.go | 22 +- management/server/store/store.go | 2 +- management/server/types/firewall_rule.go | 14 +- management/server/types/network.go | 70 + management/server/types/policyrule.go | 19 + .../server/types/route_firewall_rule.go | 25 + management/server/util/util.go | 31 + management/server/util/util_test.go | 41 + route/route.go | 4 +- 84 files changed, 4471 insertions(+), 1196 deletions(-) create mode 100644 client/cmd/forwarding_rules.go create mode 100644 client/firewall/manager/forward_rule.go create mode 100644 client/firewall/manager/protocol.go create mode 100644 client/internal/ingressgw/manager.go create mode 100644 client/internal/ingressgw/manager_test.go create mode 100644 client/internal/message_convert.go create mode 100644 client/internal/routemanager/ipfwdstate/ipfwdstate.go create mode 100644 client/server/forwardingrules.go create mode 100644 management/domain/list_test.go rename management/server/{ => integrations}/integrated_validator/interface.go (100%) create mode 100644 management/server/integrations/port_forwarding/controller.go create mode 100644 management/server/peers/manager.go create mode 100644 management/server/util/util_test.go diff --git a/.github/workflows/golang-test-linux.yml b/.github/workflows/golang-test-linux.yml index 3be8bcff3..cf061f876 100644 --- a/.github/workflows/golang-test-linux.yml +++ b/.github/workflows/golang-test-linux.yml @@ -258,7 +258,7 @@ jobs: strategy: fail-fast: false matrix: - arch: [ '386','amd64' ] + arch: [ 'amd64' ] store: [ 'sqlite', 'postgres', 'mysql' ] runs-on: ubuntu-22.04 steps: @@ -325,8 +325,8 @@ jobs: strategy: fail-fast: false matrix: - arch: [ '386','amd64' ] - store: [ 'sqlite', 'postgres', 'mysql' ] + arch: [ 'amd64' ] + store: [ 'sqlite', 'postgres' ] runs-on: ubuntu-22.04 steps: - name: Install Go @@ -392,7 +392,7 @@ jobs: strategy: fail-fast: false matrix: - arch: [ '386','amd64' ] + arch: [ 'amd64' ] store: [ 'sqlite', 'postgres' ] runs-on: ubuntu-22.04 steps: @@ -461,7 +461,7 @@ jobs: strategy: fail-fast: false matrix: - arch: [ '386','amd64' ] + arch: [ 'amd64' ] store: [ 'sqlite', 'postgres'] runs-on: ubuntu-22.04 steps: diff --git a/client/cmd/forwarding_rules.go b/client/cmd/forwarding_rules.go new file mode 100644 index 000000000..b3052746a --- /dev/null +++ b/client/cmd/forwarding_rules.go @@ -0,0 +1,98 @@ +package cmd + +import ( + "fmt" + "sort" + + "github.com/spf13/cobra" + "google.golang.org/grpc/status" + + "github.com/netbirdio/netbird/client/proto" +) + +var forwardingRulesCmd = &cobra.Command{ + Use: "forwarding", + Short: "List forwarding rules", + Long: `Commands to list forwarding rules.`, +} + +var forwardingRulesListCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List forwarding rules", + Example: " netbird forwarding list", + Long: "Commands to list forwarding rules.", + RunE: listForwardingRules, +} + +func listForwardingRules(cmd *cobra.Command, _ []string) error { + conn, err := getClient(cmd) + if err != nil { + return err + } + defer conn.Close() + + client := proto.NewDaemonServiceClient(conn) + resp, err := client.ForwardingRules(cmd.Context(), &proto.EmptyRequest{}) + if err != nil { + return fmt.Errorf("failed to list network: %v", status.Convert(err).Message()) + } + + if len(resp.GetRules()) == 0 { + cmd.Println("No forwarding rules available.") + return nil + } + + printForwardingRules(cmd, resp.GetRules()) + return nil +} + +func printForwardingRules(cmd *cobra.Command, rules []*proto.ForwardingRule) { + cmd.Println("Available forwarding rules:") + + // Sort rules by translated address + sort.Slice(rules, func(i, j int) bool { + if rules[i].GetTranslatedAddress() != rules[j].GetTranslatedAddress() { + return rules[i].GetTranslatedAddress() < rules[j].GetTranslatedAddress() + } + if rules[i].GetProtocol() != rules[j].GetProtocol() { + return rules[i].GetProtocol() < rules[j].GetProtocol() + } + + return getFirstPort(rules[i].GetDestinationPort()) < getFirstPort(rules[j].GetDestinationPort()) + }) + + var lastIP string + for _, rule := range rules { + dPort := portToString(rule.GetDestinationPort()) + tPort := portToString(rule.GetTranslatedPort()) + if lastIP != rule.GetTranslatedAddress() { + lastIP = rule.GetTranslatedAddress() + cmd.Printf("\nTranslated peer: %s\n", rule.GetTranslatedHostname()) + } + + cmd.Printf(" Local %s/%s to %s:%s\n", rule.GetProtocol(), dPort, rule.GetTranslatedAddress(), tPort) + } +} + +func getFirstPort(portInfo *proto.PortInfo) int { + switch v := portInfo.PortSelection.(type) { + case *proto.PortInfo_Port: + return int(v.Port) + case *proto.PortInfo_Range_: + return int(v.Range.GetStart()) + default: + return 0 + } +} + +func portToString(translatedPort *proto.PortInfo) string { + switch v := translatedPort.PortSelection.(type) { + case *proto.PortInfo_Port: + return fmt.Sprintf("%d", v.Port) + case *proto.PortInfo_Range_: + return fmt.Sprintf("%d-%d", v.Range.GetStart(), v.Range.GetEnd()) + default: + return "No port specified" + } +} diff --git a/client/cmd/root.go b/client/cmd/root.go index b25c2750c..f088192f1 100644 --- a/client/cmd/root.go +++ b/client/cmd/root.go @@ -145,6 +145,7 @@ func init() { rootCmd.AddCommand(versionCmd) rootCmd.AddCommand(sshCmd) rootCmd.AddCommand(networksCMD) + rootCmd.AddCommand(forwardingRulesCmd) rootCmd.AddCommand(debugCmd) serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service @@ -153,6 +154,8 @@ func init() { networksCMD.AddCommand(routesListCmd) networksCMD.AddCommand(routesSelectCmd, routesDeselectCmd) + forwardingRulesCmd.AddCommand(forwardingRulesListCmd) + debugCmd.AddCommand(debugBundleCmd) debugCmd.AddCommand(logCmd) logCmd.AddCommand(logLevelCmd) diff --git a/client/cmd/testutil_test.go b/client/cmd/testutil_test.go index e0d784048..4c06a7da0 100644 --- a/client/cmd/testutil_test.go +++ b/client/cmd/testutil_test.go @@ -10,6 +10,7 @@ import ( "go.opentelemetry.io/otel" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/settings" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" @@ -89,7 +90,7 @@ func startManagement(t *testing.T, config *mgmt.Config, testFile string) (*grpc. metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) require.NoError(t, err) - accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics) + accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock()) if err != nil { t.Fatal(err) } diff --git a/client/firewall/iptables/acl_linux.go b/client/firewall/iptables/acl_linux.go index 6c4895e05..8f1b231b8 100644 --- a/client/firewall/iptables/acl_linux.go +++ b/client/firewall/iptables/acl_linux.go @@ -30,10 +30,8 @@ type entry struct { } type aclManager struct { - iptablesClient *iptables.IPTables - wgIface iFaceMapper - routingFwChainName string - + iptablesClient *iptables.IPTables + wgIface iFaceMapper entries aclEntries optionalEntries map[string][]entry ipsetStore *ipsetStore @@ -41,12 +39,10 @@ type aclManager struct { stateManager *statemanager.Manager } -func newAclManager(iptablesClient *iptables.IPTables, wgIface iFaceMapper, routingFwChainName string) (*aclManager, error) { +func newAclManager(iptablesClient *iptables.IPTables, wgIface iFaceMapper) (*aclManager, error) { m := &aclManager{ - iptablesClient: iptablesClient, - wgIface: wgIface, - routingFwChainName: routingFwChainName, - + iptablesClient: iptablesClient, + wgIface: wgIface, entries: make(map[string][][]string), optionalEntries: make(map[string][]entry), ipsetStore: newIpsetStore(), @@ -314,9 +310,12 @@ func (m *aclManager) seedInitialEntries() { m.appendToEntries("INPUT", []string{"-i", m.wgIface.Name(), "-j", chainNameInputRules}) m.appendToEntries("INPUT", append([]string{"-i", m.wgIface.Name()}, established...)) + // Inbound is handled by our ACLs, the rest is dropped. + // For outbound we respect the FORWARD policy. However, we need to allow established/related traffic for inbound rules. m.appendToEntries("FORWARD", []string{"-i", m.wgIface.Name(), "-j", "DROP"}) - m.appendToEntries("FORWARD", []string{"-i", m.wgIface.Name(), "-j", m.routingFwChainName}) - m.appendToEntries("FORWARD", append([]string{"-o", m.wgIface.Name()}, established...)) + + m.appendToEntries("FORWARD", []string{"-o", m.wgIface.Name(), "-j", chainRTFWDOUT}) + m.appendToEntries("FORWARD", []string{"-i", m.wgIface.Name(), "-j", chainRTFWDIN}) } func (m *aclManager) seedInitialOptionalEntries() { diff --git a/client/firewall/iptables/manager_linux.go b/client/firewall/iptables/manager_linux.go index 144d5a17f..42450fc28 100644 --- a/client/firewall/iptables/manager_linux.go +++ b/client/firewall/iptables/manager_linux.go @@ -52,7 +52,7 @@ func Create(wgIface iFaceMapper) (*Manager, error) { return nil, fmt.Errorf("create router: %w", err) } - m.aclMgr, err = newAclManager(iptablesClient, wgIface, chainRTFWD) + m.aclMgr, err = newAclManager(iptablesClient, wgIface) if err != nil { return nil, fmt.Errorf("create acl manager: %w", err) } @@ -226,6 +226,22 @@ func (m *Manager) DisableRouting() error { return nil } +// AddDNATRule adds a DNAT rule +func (m *Manager) AddDNATRule(rule firewall.ForwardRule) (firewall.Rule, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + return m.router.AddDNATRule(rule) +} + +// DeleteDNATRule deletes a DNAT rule +func (m *Manager) DeleteDNATRule(rule firewall.Rule) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + return m.router.DeleteDNATRule(rule) +} + func getConntrackEstablished() []string { return []string{"-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"} } diff --git a/client/firewall/iptables/router_linux.go b/client/firewall/iptables/router_linux.go index 6522daa3f..cc2c25e55 100644 --- a/client/firewall/iptables/router_linux.go +++ b/client/firewall/iptables/router_linux.go @@ -16,6 +16,7 @@ import ( nberrors "github.com/netbirdio/netbird/client/errors" firewall "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/internal/acl/id" + "github.com/netbirdio/netbird/client/internal/routemanager/ipfwdstate" "github.com/netbirdio/netbird/client/internal/routemanager/refcounter" "github.com/netbirdio/netbird/client/internal/statemanager" nbnet "github.com/netbirdio/netbird/util/net" @@ -23,22 +24,36 @@ import ( // constants needed to manage and create iptable rules const ( - tableFilter = "filter" - tableNat = "nat" - tableMangle = "mangle" + tableFilter = "filter" + tableNat = "nat" + tableMangle = "mangle" + chainPOSTROUTING = "POSTROUTING" chainPREROUTING = "PREROUTING" chainRTNAT = "NETBIRD-RT-NAT" - chainRTFWD = "NETBIRD-RT-FWD" + chainRTFWDIN = "NETBIRD-RT-FWD-IN" + chainRTFWDOUT = "NETBIRD-RT-FWD-OUT" chainRTPRE = "NETBIRD-RT-PRE" + chainRTRDR = "NETBIRD-RT-RDR" routingFinalForwardJump = "ACCEPT" routingFinalNatJump = "MASQUERADE" - jumpPre = "jump-pre" - jumpNat = "jump-nat" - matchSet = "--match-set" + jumpManglePre = "jump-mangle-pre" + jumpNatPre = "jump-nat-pre" + jumpNatPost = "jump-nat-post" + matchSet = "--match-set" + + dnatSuffix = "_dnat" + snatSuffix = "_snat" + fwdSuffix = "_fwd" ) +type ruleInfo struct { + chain string + table string + rule []string +} + type routeFilteringRuleParams struct { Sources []netip.Prefix Destination netip.Prefix @@ -62,6 +77,7 @@ type router struct { legacyManagement bool stateManager *statemanager.Manager + ipFwdState *ipfwdstate.IPForwardingState } func newRouter(iptablesClient *iptables.IPTables, wgIface iFaceMapper) (*router, error) { @@ -69,6 +85,7 @@ func newRouter(iptablesClient *iptables.IPTables, wgIface iFaceMapper) (*router, iptablesClient: iptablesClient, rules: make(map[string][]string), wgIface: wgIface, + ipFwdState: ipfwdstate.NewIPForwardingState(), } r.ipsetCounter = refcounter.New( @@ -139,9 +156,9 @@ func (r *router) AddRouteFiltering( var err error if action == firewall.ActionDrop { // after the established rule - err = r.iptablesClient.Insert(tableFilter, chainRTFWD, 2, rule...) + err = r.iptablesClient.Insert(tableFilter, chainRTFWDIN, 2, rule...) } else { - err = r.iptablesClient.Append(tableFilter, chainRTFWD, rule...) + err = r.iptablesClient.Append(tableFilter, chainRTFWDIN, rule...) } if err != nil { @@ -156,12 +173,12 @@ func (r *router) AddRouteFiltering( } func (r *router) DeleteRouteRule(rule firewall.Rule) error { - ruleKey := rule.GetRuleID() + ruleKey := rule.ID() if rule, exists := r.rules[ruleKey]; exists { setName := r.findSetNameInRule(rule) - if err := r.iptablesClient.Delete(tableFilter, chainRTFWD, rule...); err != nil { + if err := r.iptablesClient.Delete(tableFilter, chainRTFWDIN, rule...); err != nil { return fmt.Errorf("delete route rule: %v", err) } delete(r.rules, ruleKey) @@ -212,6 +229,10 @@ func (r *router) deleteIpSet(setName string) error { // AddNatRule inserts an iptables rule pair into the nat chain func (r *router) AddNatRule(pair firewall.RouterPair) error { + if err := r.ipFwdState.RequestForwarding(); err != nil { + return err + } + if r.legacyManagement { log.Warnf("This peer is connected to a NetBird Management service with an older version. Allowing all traffic for %s", pair.Destination) if err := r.addLegacyRouteRule(pair); err != nil { @@ -238,6 +259,10 @@ func (r *router) AddNatRule(pair firewall.RouterPair) error { // RemoveNatRule removes an iptables rule pair from forwarding and nat chains func (r *router) RemoveNatRule(pair firewall.RouterPair) error { + if err := r.ipFwdState.ReleaseForwarding(); err != nil { + log.Errorf("%v", err) + } + if err := r.removeNatRule(pair); err != nil { return fmt.Errorf("remove nat rule: %w", err) } @@ -264,7 +289,7 @@ func (r *router) addLegacyRouteRule(pair firewall.RouterPair) error { } rule := []string{"-s", pair.Source.String(), "-d", pair.Destination.String(), "-j", routingFinalForwardJump} - if err := r.iptablesClient.Append(tableFilter, chainRTFWD, rule...); err != nil { + if err := r.iptablesClient.Append(tableFilter, chainRTFWDIN, rule...); err != nil { return fmt.Errorf("add legacy forwarding rule %s -> %s: %v", pair.Source, pair.Destination, err) } @@ -277,7 +302,7 @@ func (r *router) removeLegacyRouteRule(pair firewall.RouterPair) error { ruleKey := firewall.GenKey(firewall.ForwardingFormat, pair) if rule, exists := r.rules[ruleKey]; exists { - if err := r.iptablesClient.DeleteIfExists(tableFilter, chainRTFWD, rule...); err != nil { + if err := r.iptablesClient.DeleteIfExists(tableFilter, chainRTFWDIN, rule...); err != nil { return fmt.Errorf("remove legacy forwarding rule %s -> %s: %v", pair.Source, pair.Destination, err) } delete(r.rules, ruleKey) @@ -305,7 +330,7 @@ func (r *router) RemoveAllLegacyRouteRules() error { if !strings.HasPrefix(k, firewall.ForwardingFormatPrefix) { continue } - if err := r.iptablesClient.DeleteIfExists(tableFilter, chainRTFWD, rule...); err != nil { + if err := r.iptablesClient.DeleteIfExists(tableFilter, chainRTFWDIN, rule...); err != nil { merr = multierror.Append(merr, fmt.Errorf("remove legacy forwarding rule: %v", err)) } else { delete(r.rules, k) @@ -343,9 +368,11 @@ func (r *router) cleanUpDefaultForwardRules() error { chain string table string }{ - {chainRTFWD, tableFilter}, - {chainRTNAT, tableNat}, + {chainRTFWDIN, tableFilter}, + {chainRTFWDOUT, tableFilter}, {chainRTPRE, tableMangle}, + {chainRTNAT, tableNat}, + {chainRTRDR, tableNat}, } { ok, err := r.iptablesClient.ChainExists(chainInfo.table, chainInfo.chain) if err != nil { @@ -365,16 +392,22 @@ func (r *router) createContainers() error { chain string table string }{ - {chainRTFWD, tableFilter}, + {chainRTFWDIN, tableFilter}, + {chainRTFWDOUT, tableFilter}, {chainRTPRE, tableMangle}, {chainRTNAT, tableNat}, + {chainRTRDR, tableNat}, } { - if err := r.createAndSetupChain(chainInfo.chain); err != nil { + if err := r.iptablesClient.NewChain(chainInfo.table, chainInfo.chain); err != nil { return fmt.Errorf("create chain %s in table %s: %w", chainInfo.chain, chainInfo.table, err) } } - if err := r.insertEstablishedRule(chainRTFWD); err != nil { + if err := r.insertEstablishedRule(chainRTFWDIN); err != nil { + return fmt.Errorf("insert established rule: %w", err) + } + + if err := r.insertEstablishedRule(chainRTFWDOUT); err != nil { return fmt.Errorf("insert established rule: %w", err) } @@ -415,27 +448,6 @@ func (r *router) addPostroutingRules() error { return nil } -func (r *router) createAndSetupChain(chain string) error { - table := r.getTableForChain(chain) - - if err := r.iptablesClient.NewChain(table, chain); err != nil { - return fmt.Errorf("failed creating chain %s, error: %v", chain, err) - } - - return nil -} - -func (r *router) getTableForChain(chain string) string { - switch chain { - case chainRTNAT: - return tableNat - case chainRTPRE: - return tableMangle - default: - return tableFilter - } -} - func (r *router) insertEstablishedRule(chain string) error { establishedRule := getConntrackEstablished() @@ -454,28 +466,43 @@ func (r *router) addJumpRules() error { // Jump to NAT chain natRule := []string{"-j", chainRTNAT} if err := r.iptablesClient.Insert(tableNat, chainPOSTROUTING, 1, natRule...); err != nil { - return fmt.Errorf("add nat jump rule: %v", err) + return fmt.Errorf("add nat postrouting jump rule: %v", err) } - r.rules[jumpNat] = natRule + r.rules[jumpNatPost] = natRule - // Jump to prerouting chain + // Jump to mangle prerouting chain preRule := []string{"-j", chainRTPRE} if err := r.iptablesClient.Insert(tableMangle, chainPREROUTING, 1, preRule...); err != nil { - return fmt.Errorf("add prerouting jump rule: %v", err) + return fmt.Errorf("add mangle prerouting jump rule: %v", err) } - r.rules[jumpPre] = preRule + r.rules[jumpManglePre] = preRule + + // Jump to nat prerouting chain + rdrRule := []string{"-j", chainRTRDR} + if err := r.iptablesClient.Insert(tableNat, chainPREROUTING, 1, rdrRule...); err != nil { + return fmt.Errorf("add nat prerouting jump rule: %v", err) + } + r.rules[jumpNatPre] = rdrRule return nil } func (r *router) cleanJumpRules() error { - for _, ruleKey := range []string{jumpNat, jumpPre} { + for _, ruleKey := range []string{jumpNatPost, jumpManglePre, jumpNatPre} { if rule, exists := r.rules[ruleKey]; exists { - table := tableNat - chain := chainPOSTROUTING - if ruleKey == jumpPre { + var table, chain string + switch ruleKey { + case jumpNatPost: + table = tableNat + chain = chainPOSTROUTING + case jumpManglePre: table = tableMangle chain = chainPREROUTING + case jumpNatPre: + table = tableNat + chain = chainPREROUTING + default: + return fmt.Errorf("unknown jump rule: %s", ruleKey) } if err := r.iptablesClient.DeleteIfExists(table, chain, rule...); err != nil { @@ -520,6 +547,8 @@ func (r *router) addNatRule(pair firewall.RouterPair) error { } r.rules[ruleKey] = rule + + r.updateState() return nil } @@ -535,6 +564,7 @@ func (r *router) removeNatRule(pair firewall.RouterPair) error { log.Debugf("marking rule %s not found", ruleKey) } + r.updateState() return nil } @@ -564,6 +594,137 @@ func (r *router) updateState() { } } +func (r *router) AddDNATRule(rule firewall.ForwardRule) (firewall.Rule, error) { + if err := r.ipFwdState.RequestForwarding(); err != nil { + return nil, err + } + + ruleKey := rule.ID() + if _, exists := r.rules[ruleKey+dnatSuffix]; exists { + return rule, nil + } + + toDestination := rule.TranslatedAddress.String() + switch { + case len(rule.TranslatedPort.Values) == 0: + // no translated port, use original port + case len(rule.TranslatedPort.Values) == 1: + toDestination += fmt.Sprintf(":%d", rule.TranslatedPort.Values[0]) + case rule.TranslatedPort.IsRange && len(rule.TranslatedPort.Values) == 2: + // need the "/originalport" suffix to avoid dnat port randomization + toDestination += fmt.Sprintf(":%d-%d/%d", rule.TranslatedPort.Values[0], rule.TranslatedPort.Values[1], rule.DestinationPort.Values[0]) + default: + return nil, fmt.Errorf("invalid translated port: %v", rule.TranslatedPort) + } + + proto := strings.ToLower(string(rule.Protocol)) + + rules := make(map[string]ruleInfo, 3) + + // DNAT rule + dnatRule := []string{ + "!", "-i", r.wgIface.Name(), + "-p", proto, + "-j", "DNAT", + "--to-destination", toDestination, + } + dnatRule = append(dnatRule, applyPort("--dport", &rule.DestinationPort)...) + rules[ruleKey+dnatSuffix] = ruleInfo{ + table: tableNat, + chain: chainRTRDR, + rule: dnatRule, + } + + // SNAT rule + snatRule := []string{ + "-o", r.wgIface.Name(), + "-p", proto, + "-d", rule.TranslatedAddress.String(), + "-j", "MASQUERADE", + } + snatRule = append(snatRule, applyPort("--dport", &rule.TranslatedPort)...) + rules[ruleKey+snatSuffix] = ruleInfo{ + table: tableNat, + chain: chainRTNAT, + rule: snatRule, + } + + // Forward filtering rule, if fwd policy is DROP + forwardRule := []string{ + "-o", r.wgIface.Name(), + "-p", proto, + "-d", rule.TranslatedAddress.String(), + "-j", "ACCEPT", + } + forwardRule = append(forwardRule, applyPort("--dport", &rule.TranslatedPort)...) + rules[ruleKey+fwdSuffix] = ruleInfo{ + table: tableFilter, + chain: chainRTFWDOUT, + rule: forwardRule, + } + + for key, ruleInfo := range rules { + if err := r.iptablesClient.Append(ruleInfo.table, ruleInfo.chain, ruleInfo.rule...); err != nil { + if rollbackErr := r.rollbackRules(rules); rollbackErr != nil { + log.Errorf("rollback failed: %v", rollbackErr) + } + return nil, fmt.Errorf("add rule %s: %w", key, err) + } + r.rules[key] = ruleInfo.rule + } + + r.updateState() + return rule, nil +} + +func (r *router) rollbackRules(rules map[string]ruleInfo) error { + var merr *multierror.Error + for key, ruleInfo := range rules { + if err := r.iptablesClient.DeleteIfExists(ruleInfo.table, ruleInfo.chain, ruleInfo.rule...); err != nil { + merr = multierror.Append(merr, fmt.Errorf("rollback rule %s: %w", key, err)) + // On rollback error, add to rules map for next cleanup + r.rules[key] = ruleInfo.rule + } + } + if merr != nil { + r.updateState() + } + return nberrors.FormatErrorOrNil(merr) +} + +func (r *router) DeleteDNATRule(rule firewall.Rule) error { + if err := r.ipFwdState.ReleaseForwarding(); err != nil { + log.Errorf("%v", err) + } + + ruleKey := rule.ID() + + var merr *multierror.Error + if dnatRule, exists := r.rules[ruleKey+dnatSuffix]; exists { + if err := r.iptablesClient.Delete(tableNat, chainRTRDR, dnatRule...); err != nil { + merr = multierror.Append(merr, fmt.Errorf("delete DNAT rule: %w", err)) + } + delete(r.rules, ruleKey+dnatSuffix) + } + + if snatRule, exists := r.rules[ruleKey+snatSuffix]; exists { + if err := r.iptablesClient.Delete(tableNat, chainRTNAT, snatRule...); err != nil { + merr = multierror.Append(merr, fmt.Errorf("delete SNAT rule: %w", err)) + } + delete(r.rules, ruleKey+snatSuffix) + } + + if fwdRule, exists := r.rules[ruleKey+fwdSuffix]; exists { + if err := r.iptablesClient.Delete(tableFilter, chainRTFWDIN, fwdRule...); err != nil { + merr = multierror.Append(merr, fmt.Errorf("delete forward rule: %w", err)) + } + delete(r.rules, ruleKey+fwdSuffix) + } + + r.updateState() + return nberrors.FormatErrorOrNil(merr) +} + func genRouteFilteringRuleSpec(params routeFilteringRuleParams) []string { var rule []string diff --git a/client/firewall/iptables/router_linux_test.go b/client/firewall/iptables/router_linux_test.go index 0eb207567..3f132504f 100644 --- a/client/firewall/iptables/router_linux_test.go +++ b/client/firewall/iptables/router_linux_test.go @@ -39,12 +39,14 @@ func TestIptablesManager_RestoreOrCreateContainers(t *testing.T) { }() // Now 5 rules: - // 1. established rule in forward chain - // 2. jump rule to NAT chain - // 3. jump rule to PRE chain - // 4. static outbound masquerade rule - // 5. static return masquerade rule - require.Len(t, manager.rules, 5, "should have created rules map") + // 1. established rule forward in + // 2. estbalished rule forward out + // 3. jump rule to POST nat chain + // 4. jump rule to PRE mangle chain + // 5. jump rule to PRE nat chain + // 6. static outbound masquerade rule + // 7. static return masquerade rule + require.Len(t, manager.rules, 7, "should have created rules map") exists, err := manager.iptablesClient.Exists(tableNat, chainPOSTROUTING, "-j", chainRTNAT) require.NoError(t, err, "should be able to query the iptables %s table and %s chain", tableNat, chainPOSTROUTING) @@ -332,14 +334,14 @@ func TestRouter_AddRouteFiltering(t *testing.T) { require.NoError(t, err, "AddRouteFiltering failed") // Check if the rule is in the internal map - rule, ok := r.rules[ruleKey.GetRuleID()] + rule, ok := r.rules[ruleKey.ID()] assert.True(t, ok, "Rule not found in internal map") // Log the internal rule t.Logf("Internal rule: %v", rule) // Check if the rule exists in iptables - exists, err := iptablesClient.Exists(tableFilter, chainRTFWD, rule...) + exists, err := iptablesClient.Exists(tableFilter, chainRTFWDIN, rule...) assert.NoError(t, err, "Failed to check rule existence") assert.True(t, exists, "Rule not found in iptables") diff --git a/client/firewall/iptables/rule.go b/client/firewall/iptables/rule.go index e90e32f8b..aa4d2d079 100644 --- a/client/firewall/iptables/rule.go +++ b/client/firewall/iptables/rule.go @@ -12,6 +12,6 @@ type Rule struct { } // GetRuleID returns the rule id -func (r *Rule) GetRuleID() string { +func (r *Rule) ID() string { return r.ruleID } diff --git a/client/firewall/manager/firewall.go b/client/firewall/manager/firewall.go index e71328a44..cf2387840 100644 --- a/client/firewall/manager/firewall.go +++ b/client/firewall/manager/firewall.go @@ -26,8 +26,8 @@ const ( // Each firewall type for different OS can use different type // of the properties to hold data of the created rule type Rule interface { - // GetRuleID returns the rule id - GetRuleID() string + // ID returns the rule id + ID() string } // RuleDirection is the traffic direction which a rule is applied @@ -105,6 +105,12 @@ type Manager interface { EnableRouting() error DisableRouting() error + + // AddDNATRule adds a DNAT rule + AddDNATRule(ForwardRule) (Rule, error) + + // DeleteDNATRule deletes a DNAT rule + DeleteDNATRule(Rule) error } func GenKey(format string, pair RouterPair) string { diff --git a/client/firewall/manager/forward_rule.go b/client/firewall/manager/forward_rule.go new file mode 100644 index 000000000..21a43520e --- /dev/null +++ b/client/firewall/manager/forward_rule.go @@ -0,0 +1,27 @@ +package manager + +import ( + "fmt" + "net/netip" +) + +// ForwardRule todo figure out better place to this to avoid circular imports +type ForwardRule struct { + Protocol Protocol + DestinationPort Port + TranslatedAddress netip.Addr + TranslatedPort Port +} + +func (r ForwardRule) ID() string { + id := fmt.Sprintf("%s;%s;%s;%s", + r.Protocol, + r.DestinationPort.String(), + r.TranslatedAddress.String(), + r.TranslatedPort.String()) + return id +} + +func (r ForwardRule) String() string { + return fmt.Sprintf("protocol: %s, destinationPort: %s, translatedAddress: %s, translatedPort: %s", r.Protocol, r.DestinationPort.String(), r.TranslatedAddress.String(), r.TranslatedPort.String()) +} diff --git a/client/firewall/manager/port.go b/client/firewall/manager/port.go index df02e3117..d87fd09ef 100644 --- a/client/firewall/manager/port.go +++ b/client/firewall/manager/port.go @@ -1,30 +1,12 @@ package manager import ( + "fmt" "strconv" ) -// Protocol is the protocol of the port -type Protocol string - -const ( - // ProtocolTCP is the TCP protocol - ProtocolTCP Protocol = "tcp" - - // ProtocolUDP is the UDP protocol - ProtocolUDP Protocol = "udp" - - // ProtocolICMP is the ICMP protocol - ProtocolICMP Protocol = "icmp" - - // ProtocolALL cover all supported protocols - ProtocolALL Protocol = "all" - - // ProtocolUnknown unknown protocol - ProtocolUnknown Protocol = "unknown" -) - // Port of the address for firewall rule +// todo Move Protocol and Port and RouterPair to the Firwall package or a separate package type Port struct { // IsRange is true Values contains two values, the first is the start port, the second is the end port IsRange bool @@ -33,6 +15,25 @@ type Port struct { Values []uint16 } +func NewPort(ports ...int) (*Port, error) { + if len(ports) == 0 { + return nil, fmt.Errorf("no port provided") + } + + ports16 := make([]uint16, len(ports)) + for i, port := range ports { + if port < 1 || port > 65535 { + return nil, fmt.Errorf("invalid port number: %d (must be between 1-65535)", port) + } + ports16[i] = uint16(port) + } + + return &Port{ + IsRange: len(ports) > 1, + Values: ports16, + }, nil +} + // String interface implementation func (p *Port) String() string { var ports string diff --git a/client/firewall/manager/protocol.go b/client/firewall/manager/protocol.go new file mode 100644 index 000000000..c368fccc6 --- /dev/null +++ b/client/firewall/manager/protocol.go @@ -0,0 +1,19 @@ +package manager + +// Protocol is the protocol of the port +// todo Move Protocol and Port and RouterPair to the Firwall package or a separate package +type Protocol string + +const ( + // ProtocolTCP is the TCP protocol + ProtocolTCP Protocol = "tcp" + + // ProtocolUDP is the UDP protocol + ProtocolUDP Protocol = "udp" + + // ProtocolICMP is the ICMP protocol + ProtocolICMP Protocol = "icmp" + + // ProtocolALL cover all supported protocols + ProtocolALL Protocol = "all" +) diff --git a/client/firewall/nftables/acl_linux.go b/client/firewall/nftables/acl_linux.go index aff9e9188..4643b8a26 100644 --- a/client/firewall/nftables/acl_linux.go +++ b/client/firewall/nftables/acl_linux.go @@ -127,7 +127,7 @@ func (m *AclManager) DeletePeerRule(rule firewall.Rule) error { log.Errorf("failed to delete mangle rule: %v", err) } } - delete(m.rules, r.GetRuleID()) + delete(m.rules, r.ID()) return m.rConn.Flush() } @@ -141,7 +141,7 @@ func (m *AclManager) DeletePeerRule(rule firewall.Rule) error { log.Errorf("failed to delete mangle rule: %v", err) } } - delete(m.rules, r.GetRuleID()) + delete(m.rules, r.ID()) return m.rConn.Flush() } @@ -176,7 +176,7 @@ func (m *AclManager) DeletePeerRule(rule firewall.Rule) error { return err } - delete(m.rules, r.GetRuleID()) + delete(m.rules, r.ID()) m.ipsetStore.DeleteReferenceFromIpSet(r.nftSet.Name) if m.ipsetStore.HasReferenceToSet(r.nftSet.Name) { diff --git a/client/firewall/nftables/manager_linux.go b/client/firewall/nftables/manager_linux.go index 3df9b378d..6a8e39735 100644 --- a/client/firewall/nftables/manager_linux.go +++ b/client/firewall/nftables/manager_linux.go @@ -342,6 +342,22 @@ func (m *Manager) Flush() error { return m.aclManager.Flush() } +// AddDNATRule adds a DNAT rule +func (m *Manager) AddDNATRule(rule firewall.ForwardRule) (firewall.Rule, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + return m.router.AddDNATRule(rule) +} + +// DeleteDNATRule deletes a DNAT rule +func (m *Manager) DeleteDNATRule(rule firewall.Rule) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + return m.router.DeleteDNATRule(rule) +} + func (m *Manager) createWorkTable() (*nftables.Table, error) { tables, err := m.rConn.ListTablesOfFamily(nftables.TableFamilyIPv4) if err != nil { diff --git a/client/firewall/nftables/router_linux.go b/client/firewall/nftables/router_linux.go index 92f81f39c..6dd75ddb1 100644 --- a/client/firewall/nftables/router_linux.go +++ b/client/firewall/nftables/router_linux.go @@ -14,23 +14,31 @@ import ( "github.com/google/nftables" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" + "github.com/google/nftables/xt" "github.com/hashicorp/go-multierror" log "github.com/sirupsen/logrus" nberrors "github.com/netbirdio/netbird/client/errors" firewall "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/internal/acl/id" + "github.com/netbirdio/netbird/client/internal/routemanager/ipfwdstate" "github.com/netbirdio/netbird/client/internal/routemanager/refcounter" nbnet "github.com/netbirdio/netbird/util/net" ) const ( - chainNameRoutingFw = "netbird-rt-fwd" - chainNameRoutingNat = "netbird-rt-postrouting" - chainNameForward = "FORWARD" + tableNat = "nat" + chainNameNatPrerouting = "PREROUTING" + chainNameRoutingFw = "netbird-rt-fwd" + chainNameRoutingNat = "netbird-rt-postrouting" + chainNameRoutingRdr = "netbird-rt-redirect" + chainNameForward = "FORWARD" userDataAcceptForwardRuleIif = "frwacceptiif" userDataAcceptForwardRuleOif = "frwacceptoif" + + dnatSuffix = "_dnat" + snatSuffix = "_snat" ) const refreshRulesMapError = "refresh rules map: %w" @@ -49,16 +57,18 @@ type router struct { ipsetCounter *refcounter.Counter[string, []netip.Prefix, *nftables.Set] wgIface iFaceMapper + ipFwdState *ipfwdstate.IPForwardingState legacyManagement bool } func newRouter(workTable *nftables.Table, wgIface iFaceMapper) (*router, error) { r := &router{ - conn: &nftables.Conn{}, - workTable: workTable, - chains: make(map[string]*nftables.Chain), - rules: make(map[string]*nftables.Rule), - wgIface: wgIface, + conn: &nftables.Conn{}, + workTable: workTable, + chains: make(map[string]*nftables.Chain), + rules: make(map[string]*nftables.Rule), + wgIface: wgIface, + ipFwdState: ipfwdstate.NewIPForwardingState(), } r.ipsetCounter = refcounter.New( @@ -98,7 +108,52 @@ func (r *router) Reset() error { // clear without deleting the ipsets, the nf table will be deleted by the caller r.ipsetCounter.Clear() - return r.removeAcceptForwardRules() + var merr *multierror.Error + + if err := r.removeAcceptForwardRules(); err != nil { + merr = multierror.Append(merr, fmt.Errorf("remove accept forward rules: %w", err)) + } + + if err := r.removeNatPreroutingRules(); err != nil { + merr = multierror.Append(merr, fmt.Errorf("remove filter prerouting rules: %w", err)) + } + + return nberrors.FormatErrorOrNil(merr) +} + +func (r *router) removeNatPreroutingRules() error { + table := &nftables.Table{ + Name: tableNat, + Family: nftables.TableFamilyIPv4, + } + chain := &nftables.Chain{ + Name: chainNameNatPrerouting, + Table: table, + Hooknum: nftables.ChainHookPrerouting, + Priority: nftables.ChainPriorityNATDest, + Type: nftables.ChainTypeNAT, + } + rules, err := r.conn.GetRules(table, chain) + if err != nil { + return fmt.Errorf("get rules from nat table: %w", err) + } + + var merr *multierror.Error + + // Delete rules that have our UserData suffix + for _, rule := range rules { + if len(rule.UserData) == 0 || !strings.HasSuffix(string(rule.UserData), dnatSuffix) { + continue + } + if err := r.conn.DelRule(rule); err != nil { + merr = multierror.Append(merr, fmt.Errorf("delete rule %s: %w", rule.UserData, err)) + } + } + + if err := r.conn.Flush(); err != nil { + merr = multierror.Append(merr, fmt.Errorf(flushError, err)) + } + return nberrors.FormatErrorOrNil(merr) } func (r *router) loadFilterTable() (*nftables.Table, error) { @@ -133,14 +188,22 @@ func (r *router) createContainers() error { Type: nftables.ChainTypeNAT, }) + r.chains[chainNameRoutingRdr] = r.conn.AddChain(&nftables.Chain{ + Name: chainNameRoutingRdr, + Table: r.workTable, + Hooknum: nftables.ChainHookPrerouting, + Priority: nftables.ChainPriorityNATDest, + Type: nftables.ChainTypeNAT, + }) + // Chain is created by acl manager // TODO: move creation to a common place r.chains[chainNamePrerouting] = &nftables.Chain{ Name: chainNamePrerouting, Table: r.workTable, - Type: nftables.ChainTypeFilter, Hooknum: nftables.ChainHookPrerouting, Priority: nftables.ChainPriorityMangle, + Type: nftables.ChainTypeFilter, } // Add the single NAT rule that matches on mark @@ -281,7 +344,7 @@ func (r *router) DeleteRouteRule(rule firewall.Rule) error { return fmt.Errorf(refreshRulesMapError, err) } - ruleKey := rule.GetRuleID() + ruleKey := rule.ID() nftRule, exists := r.rules[ruleKey] if !exists { log.Debugf("route rule %s not found", ruleKey) @@ -410,6 +473,10 @@ func (r *router) deleteNftRule(rule *nftables.Rule, ruleKey string) error { // AddNatRule appends a nftables rule pair to the nat chain func (r *router) AddNatRule(pair firewall.RouterPair) error { + if err := r.ipFwdState.RequestForwarding(); err != nil { + return err + } + if err := r.refreshRulesMap(); err != nil { return fmt.Errorf(refreshRulesMapError, err) } @@ -836,6 +903,10 @@ func (r *router) removeAcceptForwardRulesIptables(ipt *iptables.IPTables) error // RemoveNatRule removes the prerouting mark rule func (r *router) RemoveNatRule(pair firewall.RouterPair) error { + if err := r.ipFwdState.ReleaseForwarding(); err != nil { + log.Errorf("%v", err) + } + if err := r.refreshRulesMap(); err != nil { return fmt.Errorf(refreshRulesMapError, err) } @@ -896,6 +967,269 @@ func (r *router) refreshRulesMap() error { return nil } +func (r *router) AddDNATRule(rule firewall.ForwardRule) (firewall.Rule, error) { + if err := r.ipFwdState.RequestForwarding(); err != nil { + return nil, err + } + + ruleKey := rule.ID() + if _, exists := r.rules[ruleKey+dnatSuffix]; exists { + return rule, nil + } + + protoNum, err := protoToInt(rule.Protocol) + if err != nil { + return nil, fmt.Errorf("convert protocol to number: %w", err) + } + + if err := r.addDnatRedirect(rule, protoNum, ruleKey); err != nil { + return nil, err + } + + r.addDnatMasq(rule, protoNum, ruleKey) + + // Unlike iptables, there's no point in adding "out" rules in the forward chain here as our policy is ACCEPT. + // To overcome DROP policies in other chains, we'd have to add rules to the chains there. + // We also cannot just add "oif accept" there and filter in our own table as we don't know what is supposed to be allowed. + // TODO: find chains with drop policies and add rules there + + if err := r.conn.Flush(); err != nil { + return nil, fmt.Errorf("flush rules: %w", err) + } + + return &rule, nil +} + +func (r *router) addDnatRedirect(rule firewall.ForwardRule, protoNum uint8, ruleKey string) error { + dnatExprs := []expr.Any{ + &expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpNeq, + Register: 1, + Data: ifname(r.wgIface.Name()), + }, + &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{protoNum}, + }, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 2, + Len: 2, + }, + } + dnatExprs = append(dnatExprs, applyPort(&rule.DestinationPort, false)...) + + // shifted translated port is not supported in nftables, so we hand this over to xtables + if rule.TranslatedPort.IsRange && len(rule.TranslatedPort.Values) == 2 { + if rule.TranslatedPort.Values[0] != rule.DestinationPort.Values[0] || + rule.TranslatedPort.Values[1] != rule.DestinationPort.Values[1] { + return r.addXTablesRedirect(dnatExprs, ruleKey, rule) + } + } + + additionalExprs, regProtoMin, regProtoMax, err := r.handleTranslatedPort(rule) + if err != nil { + return err + } + dnatExprs = append(dnatExprs, additionalExprs...) + + dnatExprs = append(dnatExprs, + &expr.NAT{ + Type: expr.NATTypeDestNAT, + Family: uint32(nftables.TableFamilyIPv4), + RegAddrMin: 1, + RegProtoMin: regProtoMin, + RegProtoMax: regProtoMax, + }, + ) + + dnatRule := &nftables.Rule{ + Table: r.workTable, + Chain: r.chains[chainNameRoutingRdr], + Exprs: dnatExprs, + UserData: []byte(ruleKey + dnatSuffix), + } + r.conn.AddRule(dnatRule) + r.rules[ruleKey+dnatSuffix] = dnatRule + + return nil +} + +func (r *router) handleTranslatedPort(rule firewall.ForwardRule) ([]expr.Any, uint32, uint32, error) { + switch { + case rule.TranslatedPort.IsRange && len(rule.TranslatedPort.Values) == 2: + return r.handlePortRange(rule) + case len(rule.TranslatedPort.Values) == 0: + return r.handleAddressOnly(rule) + case len(rule.TranslatedPort.Values) == 1: + return r.handleSinglePort(rule) + default: + return nil, 0, 0, fmt.Errorf("invalid translated port: %v", rule.TranslatedPort) + } +} + +func (r *router) handlePortRange(rule firewall.ForwardRule) ([]expr.Any, uint32, uint32, error) { + exprs := []expr.Any{ + &expr.Immediate{ + Register: 1, + Data: rule.TranslatedAddress.AsSlice(), + }, + &expr.Immediate{ + Register: 2, + Data: binaryutil.BigEndian.PutUint16(rule.TranslatedPort.Values[0]), + }, + &expr.Immediate{ + Register: 3, + Data: binaryutil.BigEndian.PutUint16(rule.TranslatedPort.Values[1]), + }, + } + return exprs, 2, 3, nil +} + +func (r *router) handleAddressOnly(rule firewall.ForwardRule) ([]expr.Any, uint32, uint32, error) { + exprs := []expr.Any{ + &expr.Immediate{ + Register: 1, + Data: rule.TranslatedAddress.AsSlice(), + }, + } + return exprs, 0, 0, nil +} + +func (r *router) handleSinglePort(rule firewall.ForwardRule) ([]expr.Any, uint32, uint32, error) { + exprs := []expr.Any{ + &expr.Immediate{ + Register: 1, + Data: rule.TranslatedAddress.AsSlice(), + }, + &expr.Immediate{ + Register: 2, + Data: binaryutil.BigEndian.PutUint16(rule.TranslatedPort.Values[0]), + }, + } + return exprs, 2, 0, nil +} + +func (r *router) addXTablesRedirect(dnatExprs []expr.Any, ruleKey string, rule firewall.ForwardRule) error { + dnatExprs = append(dnatExprs, + &expr.Counter{}, + &expr.Target{ + Name: "DNAT", + Rev: 2, + Info: &xt.NatRange2{ + NatRange: xt.NatRange{ + Flags: uint(xt.NatRangeMapIPs | xt.NatRangeProtoSpecified | xt.NatRangeProtoOffset), + MinIP: rule.TranslatedAddress.AsSlice(), + MaxIP: rule.TranslatedAddress.AsSlice(), + MinPort: rule.TranslatedPort.Values[0], + MaxPort: rule.TranslatedPort.Values[1], + }, + BasePort: rule.DestinationPort.Values[0], + }, + }, + ) + + dnatRule := &nftables.Rule{ + Table: &nftables.Table{ + Name: tableNat, + Family: nftables.TableFamilyIPv4, + }, + Chain: &nftables.Chain{ + Name: chainNameNatPrerouting, + Table: r.filterTable, + Type: nftables.ChainTypeNAT, + Hooknum: nftables.ChainHookPrerouting, + Priority: nftables.ChainPriorityNATDest, + }, + Exprs: dnatExprs, + UserData: []byte(ruleKey + dnatSuffix), + } + r.conn.AddRule(dnatRule) + r.rules[ruleKey+dnatSuffix] = dnatRule + + return nil +} + +func (r *router) addDnatMasq(rule firewall.ForwardRule, protoNum uint8, ruleKey string) { + masqExprs := []expr.Any{ + &expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: ifname(r.wgIface.Name()), + }, + &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{protoNum}, + }, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseNetworkHeader, + Offset: 16, + Len: 4, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: rule.TranslatedAddress.AsSlice(), + }, + } + + masqExprs = append(masqExprs, applyPort(&rule.TranslatedPort, false)...) + masqExprs = append(masqExprs, &expr.Masq{}) + + masqRule := &nftables.Rule{ + Table: r.workTable, + Chain: r.chains[chainNameRoutingNat], + Exprs: masqExprs, + UserData: []byte(ruleKey + snatSuffix), + } + r.conn.AddRule(masqRule) + r.rules[ruleKey+snatSuffix] = masqRule +} + +func (r *router) DeleteDNATRule(rule firewall.Rule) error { + if err := r.ipFwdState.ReleaseForwarding(); err != nil { + log.Errorf("%v", err) + } + + ruleKey := rule.ID() + + if err := r.refreshRulesMap(); err != nil { + return fmt.Errorf(refreshRulesMapError, err) + } + + var merr *multierror.Error + if dnatRule, exists := r.rules[ruleKey+dnatSuffix]; exists { + if err := r.conn.DelRule(dnatRule); err != nil { + merr = multierror.Append(merr, fmt.Errorf("delete dnat rule: %w", err)) + } + } + + if masqRule, exists := r.rules[ruleKey+snatSuffix]; exists { + if err := r.conn.DelRule(masqRule); err != nil { + merr = multierror.Append(merr, fmt.Errorf("delete snat rule: %w", err)) + } + } + + if err := r.conn.Flush(); err != nil { + merr = multierror.Append(merr, fmt.Errorf(flushError, err)) + } + + if merr == nil { + delete(r.rules, ruleKey+dnatSuffix) + delete(r.rules, ruleKey+snatSuffix) + } + + return nberrors.FormatErrorOrNil(merr) +} + // generateCIDRMatcherExpressions generates nftables expressions that matches a CIDR func generateCIDRMatcherExpressions(source bool, prefix netip.Prefix) []expr.Any { var offset uint32 @@ -959,15 +1293,11 @@ func applyPort(port *firewall.Port, isSource bool) []expr.Any { if port.IsRange && len(port.Values) == 2 { // Handle port range exprs = append(exprs, - &expr.Cmp{ - Op: expr.CmpOpGte, + &expr.Range{ + Op: expr.CmpOpEq, Register: 1, - Data: binaryutil.BigEndian.PutUint16(port.Values[0]), - }, - &expr.Cmp{ - Op: expr.CmpOpLte, - Register: 1, - Data: binaryutil.BigEndian.PutUint16(port.Values[1]), + FromData: binaryutil.BigEndian.PutUint16(port.Values[0]), + ToData: binaryutil.BigEndian.PutUint16(port.Values[1]), }, ) } else { diff --git a/client/firewall/nftables/router_linux_test.go b/client/firewall/nftables/router_linux_test.go index 9081a8349..7ead26909 100644 --- a/client/firewall/nftables/router_linux_test.go +++ b/client/firewall/nftables/router_linux_test.go @@ -319,7 +319,7 @@ func TestRouter_AddRouteFiltering(t *testing.T) { }) // Check if the rule is in the internal map - rule, ok := r.rules[ruleKey.GetRuleID()] + rule, ok := r.rules[ruleKey.ID()] assert.True(t, ok, "Rule not found in internal map") t.Log("Internal rule expressions:") @@ -336,7 +336,7 @@ func TestRouter_AddRouteFiltering(t *testing.T) { var nftRule *nftables.Rule for _, rule := range rules { - if string(rule.UserData) == ruleKey.GetRuleID() { + if string(rule.UserData) == ruleKey.ID() { nftRule = rule break } @@ -595,16 +595,20 @@ func containsPort(exprs []expr.Any, port *firewall.Port, isSource bool) bool { if ex.Base == expr.PayloadBaseTransportHeader && ex.Offset == offset && ex.Len == 2 { payloadFound = true } - case *expr.Cmp: - if port.IsRange { - if ex.Op == expr.CmpOpGte || ex.Op == expr.CmpOpLte { + case *expr.Range: + if port.IsRange && len(port.Values) == 2 { + fromPort := binary.BigEndian.Uint16(ex.FromData) + toPort := binary.BigEndian.Uint16(ex.ToData) + if fromPort == port.Values[0] && toPort == port.Values[1] { portMatchFound = true } - } else { + } + case *expr.Cmp: + if !port.IsRange { if ex.Op == expr.CmpOpEq && len(ex.Data) == 2 { portValue := binary.BigEndian.Uint16(ex.Data) for _, p := range port.Values { - if uint16(p) == portValue { + if p == portValue { portMatchFound = true break } diff --git a/client/firewall/nftables/rule_linux.go b/client/firewall/nftables/rule_linux.go index 4d652346b..a90b74e36 100644 --- a/client/firewall/nftables/rule_linux.go +++ b/client/firewall/nftables/rule_linux.go @@ -16,6 +16,6 @@ type Rule struct { } // GetRuleID returns the rule id -func (r *Rule) GetRuleID() string { +func (r *Rule) ID() string { return r.ruleID } diff --git a/client/firewall/uspfilter/rule.go b/client/firewall/uspfilter/rule.go index 6a4415f73..100c35c0a 100644 --- a/client/firewall/uspfilter/rule.go +++ b/client/firewall/uspfilter/rule.go @@ -24,8 +24,8 @@ type PeerRule struct { udpHook func([]byte) bool } -// GetRuleID returns the rule id -func (r *PeerRule) GetRuleID() string { +// ID returns the rule id +func (r *PeerRule) ID() string { return r.id } @@ -39,7 +39,7 @@ type RouteRule struct { action firewall.Action } -// GetRuleID returns the rule id -func (r *RouteRule) GetRuleID() string { +// ID returns the rule id +func (r *RouteRule) ID() string { return r.id } diff --git a/client/firewall/uspfilter/uspfilter.go b/client/firewall/uspfilter/uspfilter.go index 50f48a5c4..193526a52 100644 --- a/client/firewall/uspfilter/uspfilter.go +++ b/client/firewall/uspfilter/uspfilter.go @@ -42,6 +42,8 @@ const ( EnvEnableNetstackLocalForwarding = "NB_ENABLE_NETSTACK_LOCAL_FORWARDING" ) +var errNatNotSupported = errors.New("nat not supported with userspace firewall") + // RuleSet is a set of rules grouped by a string key type RuleSet map[string]PeerRule @@ -437,7 +439,7 @@ func (m *Manager) DeleteRouteRule(rule firewall.Rule) error { m.mutex.Lock() defer m.mutex.Unlock() - ruleID := rule.GetRuleID() + ruleID := rule.ID() idx := slices.IndexFunc(m.routeRules, func(r RouteRule) bool { return r.id == ruleID }) @@ -478,6 +480,22 @@ func (m *Manager) SetLegacyManagement(isLegacy bool) error { // Flush doesn't need to be implemented for this manager func (m *Manager) Flush() error { return nil } +// AddDNATRule adds a DNAT rule +func (m *Manager) AddDNATRule(rule firewall.ForwardRule) (firewall.Rule, error) { + if m.nativeFirewall == nil { + return nil, errNatNotSupported + } + return m.nativeFirewall.AddDNATRule(rule) +} + +// DeleteDNATRule deletes a DNAT rule +func (m *Manager) DeleteDNATRule(rule firewall.Rule) error { + if m.nativeFirewall == nil { + return errNatNotSupported + } + return m.nativeFirewall.DeleteDNATRule(rule) +} + // DropOutgoing filter outgoing packets func (m *Manager) DropOutgoing(packetData []byte) bool { return m.processOutgoingHooks(packetData) diff --git a/client/firewall/uspfilter/uspfilter_test.go b/client/firewall/uspfilter/uspfilter_test.go index c03762984..f197f4f90 100644 --- a/client/firewall/uspfilter/uspfilter_test.go +++ b/client/firewall/uspfilter/uspfilter_test.go @@ -135,7 +135,7 @@ func TestManagerDeleteRule(t *testing.T) { } for _, r := range rule2 { - if _, ok := m.incomingRules[ip.String()][r.GetRuleID()]; !ok { + if _, ok := m.incomingRules[ip.String()][r.ID()]; !ok { t.Errorf("rule2 is not in the incomingRules") } } @@ -149,7 +149,7 @@ func TestManagerDeleteRule(t *testing.T) { } for _, r := range rule2 { - if _, ok := m.incomingRules[ip.String()][r.GetRuleID()]; ok { + if _, ok := m.incomingRules[ip.String()][r.ID()]; ok { t.Errorf("rule2 is not in the incomingRules") } } diff --git a/client/internal/acl/id/id.go b/client/internal/acl/id/id.go index 8ce73655d..93f16b429 100644 --- a/client/internal/acl/id/id.go +++ b/client/internal/acl/id/id.go @@ -12,7 +12,7 @@ import ( type RuleID string -func (r RuleID) GetRuleID() string { +func (r RuleID) ID() string { return string(r) } diff --git a/client/internal/acl/manager.go b/client/internal/acl/manager.go index 31173a5f7..7cce7402f 100644 --- a/client/internal/acl/manager.go +++ b/client/internal/acl/manager.go @@ -245,7 +245,7 @@ func (d *DefaultManager) applyRouteACL(rule *mgmProto.RouteFirewallRule) (id.Rul return "", fmt.Errorf("add route rule: %w", err) } - return id.RuleID(addedRule.GetRuleID()), nil + return id.RuleID(addedRule.ID()), nil } func (d *DefaultManager) protoRuleToFirewallRule( @@ -515,7 +515,7 @@ func (d *DefaultManager) rollBack(newRulePairs map[id.RuleID][]firewall.Rule) { for _, rules := range newRulePairs { for _, rule := range rules { if err := d.firewall.DeletePeerRule(rule); err != nil { - log.Errorf("failed to delete new firewall rule (id: %v) during rollback: %v", rule.GetRuleID(), err) + log.Errorf("failed to delete new firewall rule (id: %v) during rollback: %v", rule.ID(), err) } } } diff --git a/client/internal/acl/manager_test.go b/client/internal/acl/manager_test.go index 0327d62ef..f6dc749f6 100644 --- a/client/internal/acl/manager_test.go +++ b/client/internal/acl/manager_test.go @@ -74,7 +74,7 @@ func TestDefaultManager(t *testing.T) { t.Run("add extra rules", func(t *testing.T) { existedPairs := map[string]struct{}{} for id := range acl.peerRulesPairs { - existedPairs[id.GetRuleID()] = struct{}{} + existedPairs[id.ID()] = struct{}{} } // remove first rule @@ -100,7 +100,7 @@ func TestDefaultManager(t *testing.T) { // check that old rule was removed previousCount := 0 for id := range acl.peerRulesPairs { - if _, ok := existedPairs[id.GetRuleID()]; ok { + if _, ok := existedPairs[id.ID()]; ok { previousCount++ } } diff --git a/client/internal/engine.go b/client/internal/engine.go index 943b7cd0b..cedf8364c 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -25,7 +25,7 @@ import ( nberrors "github.com/netbirdio/netbird/client/errors" "github.com/netbirdio/netbird/client/firewall" - "github.com/netbirdio/netbird/client/firewall/manager" + firewallManager "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/device" @@ -33,6 +33,7 @@ import ( "github.com/netbirdio/netbird/client/internal/acl" "github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/dnsfwd" + "github.com/netbirdio/netbird/client/internal/ingressgw" "github.com/netbirdio/netbird/client/internal/networkmonitor" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer/guard" @@ -169,10 +170,11 @@ type Engine struct { statusRecorder *peer.Status - firewall manager.Manager - routeManager routemanager.Manager - acl acl.Manager - dnsForwardMgr *dnsfwd.Manager + firewall firewallManager.Manager + routeManager routemanager.Manager + acl acl.Manager + dnsForwardMgr *dnsfwd.Manager + ingressGatewayMgr *ingressgw.Manager dnsServer dns.Server @@ -266,6 +268,13 @@ func (e *Engine) Stop() error { // stop/restore DNS first so dbus and friends don't complain because of a missing interface e.stopDNSServer() + if e.ingressGatewayMgr != nil { + if err := e.ingressGatewayMgr.Close(); err != nil { + log.Warnf("failed to cleanup forward rules: %v", err) + } + e.ingressGatewayMgr = nil + } + if e.routeManager != nil { e.routeManager.Stop(e.stateManager) } @@ -469,15 +478,15 @@ func (e *Engine) initFirewall() error { } rosenpassPort := e.rpManager.GetAddress().Port - port := manager.Port{Values: []uint16{uint16(rosenpassPort)}} + port := firewallManager.Port{Values: []uint16{uint16(rosenpassPort)}} // this rule is static and will be torn down on engine down by the firewall manager if _, err := e.firewall.AddPeerFiltering( net.IP{0, 0, 0, 0}, - manager.ProtocolUDP, + firewallManager.ProtocolUDP, nil, &port, - manager.ActionAccept, + firewallManager.ActionAccept, "", "", ); err != nil { @@ -505,10 +514,10 @@ func (e *Engine) blockLanAccess() { if _, err := e.firewall.AddRouteFiltering( []netip.Prefix{v4}, network, - manager.ProtocolALL, + firewallManager.ProtocolALL, nil, nil, - manager.ActionDrop, + firewallManager.ActionDrop, ); err != nil { merr = multierror.Append(merr, fmt.Errorf("add fw rule for network %s: %w", network, err)) } @@ -912,6 +921,11 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { log.Errorf("failed to update clientRoutes, err: %v", err) } + // Ingress forward rules + if err := e.updateForwardRules(networkMap.GetForwardingRules()); err != nil { + log.Errorf("failed to update forward rules, err: %v", err) + } + log.Debugf("got peers update from Management Service, total peers to connect to = %d", len(networkMap.GetRemotePeers())) e.updateOfflinePeers(networkMap.GetOfflinePeers()) @@ -1482,7 +1496,7 @@ func (e *Engine) GetRouteManager() routemanager.Manager { } // GetFirewallManager returns the firewall manager -func (e *Engine) GetFirewallManager() manager.Manager { +func (e *Engine) GetFirewallManager() firewallManager.Manager { return e.firewall } @@ -1770,6 +1784,74 @@ func (e *Engine) Address() (netip.Addr, error) { return ip.Unmap(), nil } +func (e *Engine) updateForwardRules(rules []*mgmProto.ForwardingRule) error { + if e.firewall == nil { + log.Warn("firewall is disabled, not updating forwarding rules") + return nil + } + + if len(rules) == 0 { + if e.ingressGatewayMgr == nil { + return nil + } + + err := e.ingressGatewayMgr.Close() + e.ingressGatewayMgr = nil + e.statusRecorder.SetIngressGwMgr(nil) + return err + } + + if e.ingressGatewayMgr == nil { + mgr := ingressgw.NewManager(e.firewall) + e.ingressGatewayMgr = mgr + e.statusRecorder.SetIngressGwMgr(mgr) + } + + var merr *multierror.Error + forwardingRules := make([]firewallManager.ForwardRule, 0, len(rules)) + for _, rule := range rules { + proto, err := convertToFirewallProtocol(rule.GetProtocol()) + if err != nil { + merr = multierror.Append(merr, fmt.Errorf("failed to convert protocol '%s': %w", rule.GetProtocol(), err)) + continue + } + + dstPortInfo, err := convertPortInfo(rule.GetDestinationPort()) + if err != nil { + merr = multierror.Append(merr, fmt.Errorf("invalid destination port '%v': %w", rule.GetDestinationPort(), err)) + continue + } + + translateIP, err := convertToIP(rule.GetTranslatedAddress()) + if err != nil { + merr = multierror.Append(merr, fmt.Errorf("failed to convert translated address '%s': %w", rule.GetTranslatedAddress(), err)) + continue + } + + translatePort, err := convertPortInfo(rule.GetTranslatedPort()) + if err != nil { + merr = multierror.Append(merr, fmt.Errorf("invalid translate port '%v': %w", rule.GetTranslatedPort(), err)) + continue + } + + forwardRule := firewallManager.ForwardRule{ + Protocol: proto, + DestinationPort: *dstPortInfo, + TranslatedAddress: translateIP, + TranslatedPort: *translatePort, + } + + forwardingRules = append(forwardingRules, forwardRule) + } + + log.Infof("updating forwarding rules: %d", len(forwardingRules)) + if err := e.ingressGatewayMgr.Update(forwardingRules); err != nil { + log.Errorf("failed to update forwarding rules: %v", err) + } + + return nberrors.FormatErrorOrNil(merr) +} + // isChecksEqual checks if two slices of checks are equal. func isChecksEqual(checks []*mgmProto.Checks, oChecks []*mgmProto.Checks) bool { for _, check := range checks { diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 54a347e31..9de1da28d 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -44,6 +44,7 @@ import ( mgmtProto "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/settings" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" @@ -1433,7 +1434,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) require.NoError(t, err) - accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics) + accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock()) if err != nil { return nil, "", err } diff --git a/client/internal/ingressgw/manager.go b/client/internal/ingressgw/manager.go new file mode 100644 index 000000000..b8952e5c0 --- /dev/null +++ b/client/internal/ingressgw/manager.go @@ -0,0 +1,107 @@ +package ingressgw + +import ( + "fmt" + "sync" + + "github.com/hashicorp/go-multierror" + log "github.com/sirupsen/logrus" + + nberrors "github.com/netbirdio/netbird/client/errors" + firewall "github.com/netbirdio/netbird/client/firewall/manager" +) + +type DNATFirewall interface { + AddDNATRule(fwdRule firewall.ForwardRule) (firewall.Rule, error) + DeleteDNATRule(rule firewall.Rule) error +} + +type RulePair struct { + firewall.ForwardRule + firewall.Rule +} + +type Manager struct { + dnatFirewall DNATFirewall + + rules map[string]RulePair // keys is the ID of the ForwardRule + rulesMu sync.Mutex +} + +func NewManager(dnatFirewall DNATFirewall) *Manager { + return &Manager{ + dnatFirewall: dnatFirewall, + rules: make(map[string]RulePair), + } +} + +func (h *Manager) Update(forwardRules []firewall.ForwardRule) error { + h.rulesMu.Lock() + defer h.rulesMu.Unlock() + + var mErr *multierror.Error + + toDelete := make(map[string]RulePair, len(h.rules)) + for id, r := range h.rules { + toDelete[id] = r + } + + // Process new/updated rules + for _, fwdRule := range forwardRules { + id := fwdRule.ID() + if _, ok := h.rules[id]; ok { + delete(toDelete, id) + continue + } + + rule, err := h.dnatFirewall.AddDNATRule(fwdRule) + if err != nil { + mErr = multierror.Append(mErr, fmt.Errorf("add forward rule '%s': %v", fwdRule.String(), err)) + continue + } + log.Infof("forward rule has been added '%s'", fwdRule) + h.rules[id] = RulePair{ + ForwardRule: fwdRule, + Rule: rule, + } + } + + // Remove deleted rules + for id, rulePair := range toDelete { + if err := h.dnatFirewall.DeleteDNATRule(rulePair.Rule); err != nil { + mErr = multierror.Append(mErr, fmt.Errorf("failed to delete forward rule '%s': %v", rulePair.ForwardRule.String(), err)) + } + log.Infof("forward rule has been deleted '%s'", rulePair.ForwardRule) + delete(h.rules, id) + } + + return nberrors.FormatErrorOrNil(mErr) +} + +func (h *Manager) Close() error { + h.rulesMu.Lock() + defer h.rulesMu.Unlock() + + log.Infof("clean up all (%d) forward rules", len(h.rules)) + var mErr *multierror.Error + for _, rule := range h.rules { + if err := h.dnatFirewall.DeleteDNATRule(rule.Rule); err != nil { + mErr = multierror.Append(mErr, fmt.Errorf("failed to delete forward rule '%s': %v", rule, err)) + } + } + + h.rules = make(map[string]RulePair) + return nberrors.FormatErrorOrNil(mErr) +} + +func (h *Manager) Rules() []firewall.ForwardRule { + h.rulesMu.Lock() + defer h.rulesMu.Unlock() + + rules := make([]firewall.ForwardRule, 0, len(h.rules)) + for _, rulePair := range h.rules { + rules = append(rules, rulePair.ForwardRule) + } + + return rules +} diff --git a/client/internal/ingressgw/manager_test.go b/client/internal/ingressgw/manager_test.go new file mode 100644 index 000000000..591ea0dd8 --- /dev/null +++ b/client/internal/ingressgw/manager_test.go @@ -0,0 +1,281 @@ +package ingressgw + +import ( + "fmt" + "net/netip" + "testing" + + firewall "github.com/netbirdio/netbird/client/firewall/manager" +) + +var ( + _ firewall.Rule = (*MocFwRule)(nil) + _ DNATFirewall = &MockDNATFirewall{} +) + +type MocFwRule struct { + id string +} + +func (m *MocFwRule) ID() string { + return string(m.id) +} + +type MockDNATFirewall struct { + throwError bool +} + +func (m *MockDNATFirewall) AddDNATRule(fwdRule firewall.ForwardRule) (firewall.Rule, error) { + if m.throwError { + return nil, fmt.Errorf("moc error") + } + + fwRule := &MocFwRule{ + id: fwdRule.ID(), + } + return fwRule, nil +} + +func (m *MockDNATFirewall) DeleteDNATRule(rule firewall.Rule) error { + if m.throwError { + return fmt.Errorf("moc error") + } + return nil +} + +func (m *MockDNATFirewall) forceToThrowErrors() { + m.throwError = true +} + +func TestManager_AddRule(t *testing.T) { + fw := &MockDNATFirewall{} + mgr := NewManager(fw) + + port, _ := firewall.NewPort(8080) + + updates := []firewall.ForwardRule{ + { + Protocol: firewall.ProtocolTCP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.1"), + TranslatedPort: *port, + }, + { + Protocol: firewall.ProtocolUDP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.1"), + TranslatedPort: *port, + }} + + if err := mgr.Update(updates); err != nil { + t.Errorf("unexpected error: %v", err) + } + + rules := mgr.Rules() + if len(rules) != len(updates) { + t.Errorf("unexpected rules count: %d", len(rules)) + } +} + +func TestManager_UpdateRule(t *testing.T) { + fw := &MockDNATFirewall{} + mgr := NewManager(fw) + + port, _ := firewall.NewPort(8080) + ruleTCP := firewall.ForwardRule{ + Protocol: firewall.ProtocolTCP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.1"), + TranslatedPort: *port, + } + + if err := mgr.Update([]firewall.ForwardRule{ruleTCP}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + ruleUDP := firewall.ForwardRule{ + Protocol: firewall.ProtocolUDP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.2"), + TranslatedPort: *port, + } + + if err := mgr.Update([]firewall.ForwardRule{ruleUDP}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + rules := mgr.Rules() + if len(rules) != 1 { + t.Errorf("unexpected rules count: %d", len(rules)) + } + + if rules[0].TranslatedAddress.String() != ruleUDP.TranslatedAddress.String() { + t.Errorf("unexpected rule: %v", rules[0]) + } + + if rules[0].TranslatedPort.String() != ruleUDP.TranslatedPort.String() { + t.Errorf("unexpected rule: %v", rules[0]) + } + + if rules[0].DestinationPort.String() != ruleUDP.DestinationPort.String() { + t.Errorf("unexpected rule: %v", rules[0]) + } + + if rules[0].Protocol != ruleUDP.Protocol { + t.Errorf("unexpected rule: %v", rules[0]) + } +} + +func TestManager_ExtendRules(t *testing.T) { + fw := &MockDNATFirewall{} + mgr := NewManager(fw) + + port, _ := firewall.NewPort(8080) + ruleTCP := firewall.ForwardRule{ + Protocol: firewall.ProtocolTCP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.1"), + TranslatedPort: *port, + } + + ruleUDP := firewall.ForwardRule{ + Protocol: firewall.ProtocolUDP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.2"), + TranslatedPort: *port, + } + + if err := mgr.Update([]firewall.ForwardRule{ruleTCP}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + if err := mgr.Update([]firewall.ForwardRule{ruleTCP, ruleUDP}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + rules := mgr.Rules() + if len(rules) != 2 { + t.Errorf("unexpected rules count: %d", len(rules)) + } +} + +func TestManager_UnderlingError(t *testing.T) { + fw := &MockDNATFirewall{} + mgr := NewManager(fw) + + port, _ := firewall.NewPort(8080) + ruleTCP := firewall.ForwardRule{ + Protocol: firewall.ProtocolTCP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.1"), + TranslatedPort: *port, + } + + ruleUDP := firewall.ForwardRule{ + Protocol: firewall.ProtocolUDP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.2"), + TranslatedPort: *port, + } + + if err := mgr.Update([]firewall.ForwardRule{ruleTCP}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + fw.forceToThrowErrors() + + if err := mgr.Update([]firewall.ForwardRule{ruleTCP, ruleUDP}); err == nil { + t.Errorf("expected error") + } + + rules := mgr.Rules() + if len(rules) != 1 { + t.Errorf("unexpected rules count: %d", len(rules)) + } +} + +func TestManager_Cleanup(t *testing.T) { + fw := &MockDNATFirewall{} + mgr := NewManager(fw) + + port, _ := firewall.NewPort(8080) + ruleTCP := firewall.ForwardRule{ + Protocol: firewall.ProtocolTCP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.1"), + TranslatedPort: *port, + } + + if err := mgr.Update([]firewall.ForwardRule{ruleTCP}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + if err := mgr.Update([]firewall.ForwardRule{}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + rules := mgr.Rules() + if len(rules) != 0 { + t.Errorf("unexpected rules count: %d", len(rules)) + } +} + +func TestManager_DeleteBrokenRule(t *testing.T) { + fw := &MockDNATFirewall{} + + // force to throw errors when Add DNAT Rule + fw.forceToThrowErrors() + mgr := NewManager(fw) + + port, _ := firewall.NewPort(8080) + ruleTCP := firewall.ForwardRule{ + Protocol: firewall.ProtocolTCP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.1"), + TranslatedPort: *port, + } + + if err := mgr.Update([]firewall.ForwardRule{ruleTCP}); err == nil { + t.Errorf("unexpected error: %v", err) + } + + rules := mgr.Rules() + if len(rules) != 0 { + t.Errorf("unexpected rules count: %d", len(rules)) + } + + // simulate that to remove a broken rule + if err := mgr.Update([]firewall.ForwardRule{}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + if err := mgr.Close(); err != nil { + t.Errorf("unexpected error: %v", err) + } +} + +func TestManager_Close(t *testing.T) { + fw := &MockDNATFirewall{} + mgr := NewManager(fw) + + port, _ := firewall.NewPort(8080) + ruleTCP := firewall.ForwardRule{ + Protocol: firewall.ProtocolTCP, + DestinationPort: *port, + TranslatedAddress: netip.MustParseAddr("172.16.254.1"), + TranslatedPort: *port, + } + + if err := mgr.Update([]firewall.ForwardRule{ruleTCP}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + if err := mgr.Close(); err != nil { + t.Errorf("unexpected error: %v", err) + } + + rules := mgr.Rules() + if len(rules) != 0 { + t.Errorf("unexpected rules count: %d", len(rules)) + } +} diff --git a/client/internal/message_convert.go b/client/internal/message_convert.go new file mode 100644 index 000000000..8ad93bfb9 --- /dev/null +++ b/client/internal/message_convert.go @@ -0,0 +1,58 @@ +package internal + +import ( + "errors" + "fmt" + "net" + "net/netip" + + firewallManager "github.com/netbirdio/netbird/client/firewall/manager" + mgmProto "github.com/netbirdio/netbird/management/proto" +) + +func convertToFirewallProtocol(protocol mgmProto.RuleProtocol) (firewallManager.Protocol, error) { + switch protocol { + case mgmProto.RuleProtocol_TCP: + return firewallManager.ProtocolTCP, nil + case mgmProto.RuleProtocol_UDP: + return firewallManager.ProtocolUDP, nil + case mgmProto.RuleProtocol_ICMP: + return firewallManager.ProtocolICMP, nil + case mgmProto.RuleProtocol_ALL: + return firewallManager.ProtocolALL, nil + default: + return "", fmt.Errorf("invalid protocol type: %s", protocol.String()) + } +} + +func convertPortInfo(portInfo *mgmProto.PortInfo) (*firewallManager.Port, error) { + if portInfo == nil { + return nil, errors.New("portInfo cannot be nil") + } + + if portInfo.GetPort() != 0 { + return firewallManager.NewPort(int(portInfo.GetPort())) + } + + if portInfo.GetRange() != nil { + return firewallManager.NewPort(int(portInfo.GetRange().Start), int(portInfo.GetRange().End)) + } + + return nil, fmt.Errorf("invalid portInfo: %v", portInfo) +} + +func convertToIP(rawIP []byte) (netip.Addr, error) { + if rawIP == nil { + return netip.Addr{}, errors.New("input bytes cannot be nil") + } + + if len(rawIP) != net.IPv4len && len(rawIP) != net.IPv6len { + return netip.Addr{}, fmt.Errorf("invalid IP length: %d", len(rawIP)) + } + + if len(rawIP) == net.IPv4len { + return netip.AddrFrom4([4]byte(rawIP)), nil + } + + return netip.AddrFrom16([16]byte(rawIP)), nil +} diff --git a/client/internal/peer/status.go b/client/internal/peer/status.go index e9976270c..ee884a76e 100644 --- a/client/internal/peer/status.go +++ b/client/internal/peer/status.go @@ -14,7 +14,9 @@ import ( gstatus "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" + firewall "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/iface/configurer" + "github.com/netbirdio/netbird/client/internal/ingressgw" "github.com/netbirdio/netbird/client/internal/relay" "github.com/netbirdio/netbird/client/proto" "github.com/netbirdio/netbird/management/domain" @@ -132,13 +134,14 @@ type NSGroupState struct { // FullStatus contains the full state held by the Status instance type FullStatus struct { - Peers []State - ManagementState ManagementState - SignalState SignalState - LocalPeerState LocalPeerState - RosenpassState RosenpassState - Relays []relay.ProbeResult - NSGroupStates []NSGroupState + Peers []State + ManagementState ManagementState + SignalState SignalState + LocalPeerState LocalPeerState + RosenpassState RosenpassState + Relays []relay.ProbeResult + NSGroupStates []NSGroupState + NumOfForwardingRules int } // Status holds a state of peers, signal, management connections and relays @@ -171,6 +174,8 @@ type Status struct { eventMux sync.RWMutex eventStreams map[string]chan *proto.SystemEvent eventQueue *EventQueue + + ingressGwMgr *ingressgw.Manager } // NewRecorder returns a new Status instance @@ -193,6 +198,12 @@ func (d *Status) SetRelayMgr(manager *relayClient.Manager) { d.relayMgr = manager } +func (d *Status) SetIngressGwMgr(ingressGwMgr *ingressgw.Manager) { + d.mux.Lock() + defer d.mux.Unlock() + d.ingressGwMgr = ingressGwMgr +} + // ReplaceOfflinePeers replaces func (d *Status) ReplaceOfflinePeers(replacement []State) { d.mux.Lock() @@ -235,6 +246,18 @@ func (d *Status) GetPeer(peerPubKey string) (State, error) { return state, nil } +func (d *Status) PeerByIP(ip string) (string, bool) { + d.mux.Lock() + defer d.mux.Unlock() + + for _, state := range d.peers { + if state.IP == ip { + return state.FQDN, true + } + } + return "", false +} + // RemovePeer removes peer from Daemon status map func (d *Status) RemovePeer(peerPubKey string) error { d.mux.Lock() @@ -734,6 +757,16 @@ func (d *Status) GetRelayStates() []relay.ProbeResult { return append(relayStates, relayState) } +func (d *Status) ForwardingRules() []firewall.ForwardRule { + d.mux.Lock() + defer d.mux.Unlock() + if d.ingressGwMgr == nil { + return nil + } + + return d.ingressGwMgr.Rules() +} + func (d *Status) GetDNSStates() []NSGroupState { d.mux.Lock() defer d.mux.Unlock() @@ -751,11 +784,12 @@ func (d *Status) GetResolvedDomainsStates() map[domain.Domain]ResolvedDomainInfo // GetFullStatus gets full status func (d *Status) GetFullStatus() FullStatus { fullStatus := FullStatus{ - ManagementState: d.GetManagementState(), - SignalState: d.GetSignalState(), - Relays: d.GetRelayStates(), - RosenpassState: d.GetRosenpassState(), - NSGroupStates: d.GetDNSStates(), + ManagementState: d.GetManagementState(), + SignalState: d.GetSignalState(), + Relays: d.GetRelayStates(), + RosenpassState: d.GetRosenpassState(), + NSGroupStates: d.GetDNSStates(), + NumOfForwardingRules: len(d.ForwardingRules()), } d.mux.Lock() diff --git a/client/internal/routemanager/client.go b/client/internal/routemanager/client.go index 2f0b78e7b..6680f727a 100644 --- a/client/internal/routemanager/client.go +++ b/client/internal/routemanager/client.go @@ -302,7 +302,7 @@ func (c *clientNetwork) recalculateRouteAndUpdatePeerAndSystem(rsn reason) error // If the chosen route is the same as the current route, do nothing if c.currentChosen != nil && c.currentChosen.ID == newChosenID && - c.currentChosen.IsEqual(c.routes[newChosenID]) { + c.currentChosen.Equal(c.routes[newChosenID]) { return nil } diff --git a/client/internal/routemanager/ipfwdstate/ipfwdstate.go b/client/internal/routemanager/ipfwdstate/ipfwdstate.go new file mode 100644 index 000000000..da81c18f9 --- /dev/null +++ b/client/internal/routemanager/ipfwdstate/ipfwdstate.go @@ -0,0 +1,51 @@ +package ipfwdstate + +import ( + "fmt" + + log "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/client/internal/routemanager/systemops" +) + +// IPForwardingState is a struct that keeps track of the IP forwarding state. +// todo: read initial state of the IP forwarding from the system and reset the state based on it +type IPForwardingState struct { + enabledCounter int +} + +func NewIPForwardingState() *IPForwardingState { + return &IPForwardingState{} +} + +func (f *IPForwardingState) RequestForwarding() error { + if f.enabledCounter != 0 { + f.enabledCounter++ + return nil + } + + if err := systemops.EnableIPForwarding(); err != nil { + return fmt.Errorf("failed to enable IP forwarding with sysctl: %w", err) + } + f.enabledCounter = 1 + log.Info("IP forwarding enabled") + + return nil +} + +func (f *IPForwardingState) ReleaseForwarding() error { + if f.enabledCounter == 0 { + return nil + } + + if f.enabledCounter > 1 { + f.enabledCounter-- + return nil + } + + // if failed to disable IP forwarding we anyway decrement the counter + f.enabledCounter = 0 + + // todo call systemops.DisableIPForwarding() + return nil +} diff --git a/client/internal/routemanager/server_nonandroid.go b/client/internal/routemanager/server_nonandroid.go index c9bbe10a6..5b6a788f8 100644 --- a/client/internal/routemanager/server_nonandroid.go +++ b/client/internal/routemanager/server_nonandroid.go @@ -13,7 +13,6 @@ import ( firewall "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/routemanager/iface" - "github.com/netbirdio/netbird/client/internal/routemanager/systemops" "github.com/netbirdio/netbird/route" ) @@ -41,7 +40,7 @@ func (m *serverRouter) updateRoutes(routesMap map[route.ID]*route.Route) error { for routeID := range m.routes { update, found := routesMap[routeID] - if !found || !update.IsEqual(m.routes[routeID]) { + if !found || !update.Equal(m.routes[routeID]) { serverRoutesToRemove = append(serverRoutesToRemove, routeID) } } @@ -71,9 +70,6 @@ func (m *serverRouter) updateRoutes(routesMap map[route.ID]*route.Route) error { } if len(m.routes) > 0 { - if err := systemops.EnableIPForwarding(); err != nil { - return fmt.Errorf("enable ip forwarding: %w", err) - } if err := m.firewall.EnableRouting(); err != nil { return fmt.Errorf("enable routing: %w", err) } diff --git a/client/proto/daemon.pb.go b/client/proto/daemon.pb.go index 55b7aa7e9..d04d7a9c0 100644 --- a/client/proto/daemon.pb.go +++ b/client/proto/daemon.pb.go @@ -136,7 +136,7 @@ func (x SystemEvent_Severity) Number() protoreflect.EnumNumber { // Deprecated: Use SystemEvent_Severity.Descriptor instead. func (SystemEvent_Severity) EnumDescriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{45, 0} + return file_daemon_proto_rawDescGZIP(), []int{49, 0} } type SystemEvent_Category int32 @@ -191,7 +191,45 @@ func (x SystemEvent_Category) Number() protoreflect.EnumNumber { // Deprecated: Use SystemEvent_Category.Descriptor instead. func (SystemEvent_Category) EnumDescriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{45, 1} + return file_daemon_proto_rawDescGZIP(), []int{49, 1} +} + +type EmptyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *EmptyRequest) Reset() { + *x = EmptyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EmptyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmptyRequest) ProtoMessage() {} + +func (x *EmptyRequest) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EmptyRequest.ProtoReflect.Descriptor instead. +func (*EmptyRequest) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{0} } type LoginRequest struct { @@ -245,7 +283,7 @@ type LoginRequest struct { func (x *LoginRequest) Reset() { *x = LoginRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[0] + mi := &file_daemon_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -258,7 +296,7 @@ func (x *LoginRequest) String() string { func (*LoginRequest) ProtoMessage() {} func (x *LoginRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[0] + mi := &file_daemon_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -271,7 +309,7 @@ func (x *LoginRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead. func (*LoginRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{0} + return file_daemon_proto_rawDescGZIP(), []int{1} } func (x *LoginRequest) GetSetupKey() string { @@ -478,7 +516,7 @@ type LoginResponse struct { func (x *LoginResponse) Reset() { *x = LoginResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[1] + mi := &file_daemon_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -491,7 +529,7 @@ func (x *LoginResponse) String() string { func (*LoginResponse) ProtoMessage() {} func (x *LoginResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[1] + mi := &file_daemon_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -504,7 +542,7 @@ func (x *LoginResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead. func (*LoginResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{1} + return file_daemon_proto_rawDescGZIP(), []int{2} } func (x *LoginResponse) GetNeedsSSOLogin() bool { @@ -547,7 +585,7 @@ type WaitSSOLoginRequest struct { func (x *WaitSSOLoginRequest) Reset() { *x = WaitSSOLoginRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[2] + mi := &file_daemon_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -560,7 +598,7 @@ func (x *WaitSSOLoginRequest) String() string { func (*WaitSSOLoginRequest) ProtoMessage() {} func (x *WaitSSOLoginRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[2] + mi := &file_daemon_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -573,7 +611,7 @@ func (x *WaitSSOLoginRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WaitSSOLoginRequest.ProtoReflect.Descriptor instead. func (*WaitSSOLoginRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{2} + return file_daemon_proto_rawDescGZIP(), []int{3} } func (x *WaitSSOLoginRequest) GetUserCode() string { @@ -599,7 +637,7 @@ type WaitSSOLoginResponse struct { func (x *WaitSSOLoginResponse) Reset() { *x = WaitSSOLoginResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[3] + mi := &file_daemon_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -612,7 +650,7 @@ func (x *WaitSSOLoginResponse) String() string { func (*WaitSSOLoginResponse) ProtoMessage() {} func (x *WaitSSOLoginResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[3] + mi := &file_daemon_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -625,7 +663,7 @@ func (x *WaitSSOLoginResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WaitSSOLoginResponse.ProtoReflect.Descriptor instead. func (*WaitSSOLoginResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{3} + return file_daemon_proto_rawDescGZIP(), []int{4} } type UpRequest struct { @@ -637,7 +675,7 @@ type UpRequest struct { func (x *UpRequest) Reset() { *x = UpRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[4] + mi := &file_daemon_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -650,7 +688,7 @@ func (x *UpRequest) String() string { func (*UpRequest) ProtoMessage() {} func (x *UpRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[4] + mi := &file_daemon_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -663,7 +701,7 @@ func (x *UpRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpRequest.ProtoReflect.Descriptor instead. func (*UpRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{4} + return file_daemon_proto_rawDescGZIP(), []int{5} } type UpResponse struct { @@ -675,7 +713,7 @@ type UpResponse struct { func (x *UpResponse) Reset() { *x = UpResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[5] + mi := &file_daemon_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -688,7 +726,7 @@ func (x *UpResponse) String() string { func (*UpResponse) ProtoMessage() {} func (x *UpResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[5] + mi := &file_daemon_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -701,7 +739,7 @@ func (x *UpResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpResponse.ProtoReflect.Descriptor instead. func (*UpResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{5} + return file_daemon_proto_rawDescGZIP(), []int{6} } type StatusRequest struct { @@ -715,7 +753,7 @@ type StatusRequest struct { func (x *StatusRequest) Reset() { *x = StatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[6] + mi := &file_daemon_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -728,7 +766,7 @@ func (x *StatusRequest) String() string { func (*StatusRequest) ProtoMessage() {} func (x *StatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[6] + mi := &file_daemon_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -741,7 +779,7 @@ func (x *StatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. func (*StatusRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{6} + return file_daemon_proto_rawDescGZIP(), []int{7} } func (x *StatusRequest) GetGetFullPeerStatus() bool { @@ -766,7 +804,7 @@ type StatusResponse struct { func (x *StatusResponse) Reset() { *x = StatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[7] + mi := &file_daemon_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -779,7 +817,7 @@ func (x *StatusResponse) String() string { func (*StatusResponse) ProtoMessage() {} func (x *StatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[7] + mi := &file_daemon_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -792,7 +830,7 @@ func (x *StatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. func (*StatusResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{7} + return file_daemon_proto_rawDescGZIP(), []int{8} } func (x *StatusResponse) GetStatus() string { @@ -825,7 +863,7 @@ type DownRequest struct { func (x *DownRequest) Reset() { *x = DownRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[8] + mi := &file_daemon_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -838,7 +876,7 @@ func (x *DownRequest) String() string { func (*DownRequest) ProtoMessage() {} func (x *DownRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[8] + mi := &file_daemon_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -851,7 +889,7 @@ func (x *DownRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DownRequest.ProtoReflect.Descriptor instead. func (*DownRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{8} + return file_daemon_proto_rawDescGZIP(), []int{9} } type DownResponse struct { @@ -863,7 +901,7 @@ type DownResponse struct { func (x *DownResponse) Reset() { *x = DownResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[9] + mi := &file_daemon_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -876,7 +914,7 @@ func (x *DownResponse) String() string { func (*DownResponse) ProtoMessage() {} func (x *DownResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[9] + mi := &file_daemon_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -889,7 +927,7 @@ func (x *DownResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DownResponse.ProtoReflect.Descriptor instead. func (*DownResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{9} + return file_daemon_proto_rawDescGZIP(), []int{10} } type GetConfigRequest struct { @@ -901,7 +939,7 @@ type GetConfigRequest struct { func (x *GetConfigRequest) Reset() { *x = GetConfigRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[10] + mi := &file_daemon_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -914,7 +952,7 @@ func (x *GetConfigRequest) String() string { func (*GetConfigRequest) ProtoMessage() {} func (x *GetConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[10] + mi := &file_daemon_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -927,7 +965,7 @@ func (x *GetConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConfigRequest.ProtoReflect.Descriptor instead. func (*GetConfigRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{10} + return file_daemon_proto_rawDescGZIP(), []int{11} } type GetConfigResponse struct { @@ -957,7 +995,7 @@ type GetConfigResponse struct { func (x *GetConfigResponse) Reset() { *x = GetConfigResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[11] + mi := &file_daemon_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -970,7 +1008,7 @@ func (x *GetConfigResponse) String() string { func (*GetConfigResponse) ProtoMessage() {} func (x *GetConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[11] + mi := &file_daemon_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -983,7 +1021,7 @@ func (x *GetConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConfigResponse.ProtoReflect.Descriptor instead. func (*GetConfigResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{11} + return file_daemon_proto_rawDescGZIP(), []int{12} } func (x *GetConfigResponse) GetManagementUrl() string { @@ -1098,7 +1136,7 @@ type PeerState struct { func (x *PeerState) Reset() { *x = PeerState{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[12] + mi := &file_daemon_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1111,7 +1149,7 @@ func (x *PeerState) String() string { func (*PeerState) ProtoMessage() {} func (x *PeerState) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[12] + mi := &file_daemon_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1124,7 +1162,7 @@ func (x *PeerState) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerState.ProtoReflect.Descriptor instead. func (*PeerState) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{12} + return file_daemon_proto_rawDescGZIP(), []int{13} } func (x *PeerState) GetIP() string { @@ -1264,7 +1302,7 @@ type LocalPeerState struct { func (x *LocalPeerState) Reset() { *x = LocalPeerState{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[13] + mi := &file_daemon_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1277,7 +1315,7 @@ func (x *LocalPeerState) String() string { func (*LocalPeerState) ProtoMessage() {} func (x *LocalPeerState) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[13] + mi := &file_daemon_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1290,7 +1328,7 @@ func (x *LocalPeerState) ProtoReflect() protoreflect.Message { // Deprecated: Use LocalPeerState.ProtoReflect.Descriptor instead. func (*LocalPeerState) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{13} + return file_daemon_proto_rawDescGZIP(), []int{14} } func (x *LocalPeerState) GetIP() string { @@ -1356,7 +1394,7 @@ type SignalState struct { func (x *SignalState) Reset() { *x = SignalState{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[14] + mi := &file_daemon_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1369,7 +1407,7 @@ func (x *SignalState) String() string { func (*SignalState) ProtoMessage() {} func (x *SignalState) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[14] + mi := &file_daemon_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1382,7 +1420,7 @@ func (x *SignalState) ProtoReflect() protoreflect.Message { // Deprecated: Use SignalState.ProtoReflect.Descriptor instead. func (*SignalState) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{14} + return file_daemon_proto_rawDescGZIP(), []int{15} } func (x *SignalState) GetURL() string { @@ -1420,7 +1458,7 @@ type ManagementState struct { func (x *ManagementState) Reset() { *x = ManagementState{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[15] + mi := &file_daemon_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1433,7 +1471,7 @@ func (x *ManagementState) String() string { func (*ManagementState) ProtoMessage() {} func (x *ManagementState) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[15] + mi := &file_daemon_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1446,7 +1484,7 @@ func (x *ManagementState) ProtoReflect() protoreflect.Message { // Deprecated: Use ManagementState.ProtoReflect.Descriptor instead. func (*ManagementState) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{15} + return file_daemon_proto_rawDescGZIP(), []int{16} } func (x *ManagementState) GetURL() string { @@ -1484,7 +1522,7 @@ type RelayState struct { func (x *RelayState) Reset() { *x = RelayState{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[16] + mi := &file_daemon_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1497,7 +1535,7 @@ func (x *RelayState) String() string { func (*RelayState) ProtoMessage() {} func (x *RelayState) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[16] + mi := &file_daemon_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1510,7 +1548,7 @@ func (x *RelayState) ProtoReflect() protoreflect.Message { // Deprecated: Use RelayState.ProtoReflect.Descriptor instead. func (*RelayState) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{16} + return file_daemon_proto_rawDescGZIP(), []int{17} } func (x *RelayState) GetURI() string { @@ -1548,7 +1586,7 @@ type NSGroupState struct { func (x *NSGroupState) Reset() { *x = NSGroupState{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[17] + mi := &file_daemon_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1561,7 +1599,7 @@ func (x *NSGroupState) String() string { func (*NSGroupState) ProtoMessage() {} func (x *NSGroupState) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[17] + mi := &file_daemon_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1574,7 +1612,7 @@ func (x *NSGroupState) ProtoReflect() protoreflect.Message { // Deprecated: Use NSGroupState.ProtoReflect.Descriptor instead. func (*NSGroupState) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{17} + return file_daemon_proto_rawDescGZIP(), []int{18} } func (x *NSGroupState) GetServers() []string { @@ -1611,19 +1649,20 @@ type FullStatus struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ManagementState *ManagementState `protobuf:"bytes,1,opt,name=managementState,proto3" json:"managementState,omitempty"` - SignalState *SignalState `protobuf:"bytes,2,opt,name=signalState,proto3" json:"signalState,omitempty"` - LocalPeerState *LocalPeerState `protobuf:"bytes,3,opt,name=localPeerState,proto3" json:"localPeerState,omitempty"` - Peers []*PeerState `protobuf:"bytes,4,rep,name=peers,proto3" json:"peers,omitempty"` - Relays []*RelayState `protobuf:"bytes,5,rep,name=relays,proto3" json:"relays,omitempty"` - DnsServers []*NSGroupState `protobuf:"bytes,6,rep,name=dns_servers,json=dnsServers,proto3" json:"dns_servers,omitempty"` - Events []*SystemEvent `protobuf:"bytes,7,rep,name=events,proto3" json:"events,omitempty"` + ManagementState *ManagementState `protobuf:"bytes,1,opt,name=managementState,proto3" json:"managementState,omitempty"` + SignalState *SignalState `protobuf:"bytes,2,opt,name=signalState,proto3" json:"signalState,omitempty"` + LocalPeerState *LocalPeerState `protobuf:"bytes,3,opt,name=localPeerState,proto3" json:"localPeerState,omitempty"` + Peers []*PeerState `protobuf:"bytes,4,rep,name=peers,proto3" json:"peers,omitempty"` + Relays []*RelayState `protobuf:"bytes,5,rep,name=relays,proto3" json:"relays,omitempty"` + DnsServers []*NSGroupState `protobuf:"bytes,6,rep,name=dns_servers,json=dnsServers,proto3" json:"dns_servers,omitempty"` + NumberOfForwardingRules int32 `protobuf:"varint,8,opt,name=NumberOfForwardingRules,proto3" json:"NumberOfForwardingRules,omitempty"` + Events []*SystemEvent `protobuf:"bytes,7,rep,name=events,proto3" json:"events,omitempty"` } func (x *FullStatus) Reset() { *x = FullStatus{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[18] + mi := &file_daemon_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1636,7 +1675,7 @@ func (x *FullStatus) String() string { func (*FullStatus) ProtoMessage() {} func (x *FullStatus) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[18] + mi := &file_daemon_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1649,7 +1688,7 @@ func (x *FullStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use FullStatus.ProtoReflect.Descriptor instead. func (*FullStatus) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{18} + return file_daemon_proto_rawDescGZIP(), []int{19} } func (x *FullStatus) GetManagementState() *ManagementState { @@ -1694,6 +1733,13 @@ func (x *FullStatus) GetDnsServers() []*NSGroupState { return nil } +func (x *FullStatus) GetNumberOfForwardingRules() int32 { + if x != nil { + return x.NumberOfForwardingRules + } + return 0 +} + func (x *FullStatus) GetEvents() []*SystemEvent { if x != nil { return x.Events @@ -1701,6 +1747,7 @@ func (x *FullStatus) GetEvents() []*SystemEvent { return nil } +// Networks type ListNetworksRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1710,7 +1757,7 @@ type ListNetworksRequest struct { func (x *ListNetworksRequest) Reset() { *x = ListNetworksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[19] + mi := &file_daemon_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1723,7 +1770,7 @@ func (x *ListNetworksRequest) String() string { func (*ListNetworksRequest) ProtoMessage() {} func (x *ListNetworksRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[19] + mi := &file_daemon_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1736,7 +1783,7 @@ func (x *ListNetworksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListNetworksRequest.ProtoReflect.Descriptor instead. func (*ListNetworksRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{19} + return file_daemon_proto_rawDescGZIP(), []int{20} } type ListNetworksResponse struct { @@ -1750,7 +1797,7 @@ type ListNetworksResponse struct { func (x *ListNetworksResponse) Reset() { *x = ListNetworksResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[20] + mi := &file_daemon_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1763,7 +1810,7 @@ func (x *ListNetworksResponse) String() string { func (*ListNetworksResponse) ProtoMessage() {} func (x *ListNetworksResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[20] + mi := &file_daemon_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1776,7 +1823,7 @@ func (x *ListNetworksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListNetworksResponse.ProtoReflect.Descriptor instead. func (*ListNetworksResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{20} + return file_daemon_proto_rawDescGZIP(), []int{21} } func (x *ListNetworksResponse) GetRoutes() []*Network { @@ -1799,7 +1846,7 @@ type SelectNetworksRequest struct { func (x *SelectNetworksRequest) Reset() { *x = SelectNetworksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[21] + mi := &file_daemon_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1812,7 +1859,7 @@ func (x *SelectNetworksRequest) String() string { func (*SelectNetworksRequest) ProtoMessage() {} func (x *SelectNetworksRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[21] + mi := &file_daemon_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1825,7 +1872,7 @@ func (x *SelectNetworksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SelectNetworksRequest.ProtoReflect.Descriptor instead. func (*SelectNetworksRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{21} + return file_daemon_proto_rawDescGZIP(), []int{22} } func (x *SelectNetworksRequest) GetNetworkIDs() []string { @@ -1858,7 +1905,7 @@ type SelectNetworksResponse struct { func (x *SelectNetworksResponse) Reset() { *x = SelectNetworksResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[22] + mi := &file_daemon_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1871,7 +1918,7 @@ func (x *SelectNetworksResponse) String() string { func (*SelectNetworksResponse) ProtoMessage() {} func (x *SelectNetworksResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[22] + mi := &file_daemon_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1884,7 +1931,7 @@ func (x *SelectNetworksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SelectNetworksResponse.ProtoReflect.Descriptor instead. func (*SelectNetworksResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{22} + return file_daemon_proto_rawDescGZIP(), []int{23} } type IPList struct { @@ -1898,7 +1945,7 @@ type IPList struct { func (x *IPList) Reset() { *x = IPList{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[23] + mi := &file_daemon_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1911,7 +1958,7 @@ func (x *IPList) String() string { func (*IPList) ProtoMessage() {} func (x *IPList) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[23] + mi := &file_daemon_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1924,7 +1971,7 @@ func (x *IPList) ProtoReflect() protoreflect.Message { // Deprecated: Use IPList.ProtoReflect.Descriptor instead. func (*IPList) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{23} + return file_daemon_proto_rawDescGZIP(), []int{24} } func (x *IPList) GetIps() []string { @@ -1949,7 +1996,7 @@ type Network struct { func (x *Network) Reset() { *x = Network{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[24] + mi := &file_daemon_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1962,7 +2009,7 @@ func (x *Network) String() string { func (*Network) ProtoMessage() {} func (x *Network) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[24] + mi := &file_daemon_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1975,7 +2022,7 @@ func (x *Network) ProtoReflect() protoreflect.Message { // Deprecated: Use Network.ProtoReflect.Descriptor instead. func (*Network) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{24} + return file_daemon_proto_rawDescGZIP(), []int{25} } func (x *Network) GetID() string { @@ -2013,6 +2060,215 @@ func (x *Network) GetResolvedIPs() map[string]*IPList { return nil } +// ForwardingRules +type PortInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to PortSelection: + // + // *PortInfo_Port + // *PortInfo_Range_ + PortSelection isPortInfo_PortSelection `protobuf_oneof:"portSelection"` +} + +func (x *PortInfo) Reset() { + *x = PortInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PortInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PortInfo) ProtoMessage() {} + +func (x *PortInfo) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PortInfo.ProtoReflect.Descriptor instead. +func (*PortInfo) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{26} +} + +func (m *PortInfo) GetPortSelection() isPortInfo_PortSelection { + if m != nil { + return m.PortSelection + } + return nil +} + +func (x *PortInfo) GetPort() uint32 { + if x, ok := x.GetPortSelection().(*PortInfo_Port); ok { + return x.Port + } + return 0 +} + +func (x *PortInfo) GetRange() *PortInfo_Range { + if x, ok := x.GetPortSelection().(*PortInfo_Range_); ok { + return x.Range + } + return nil +} + +type isPortInfo_PortSelection interface { + isPortInfo_PortSelection() +} + +type PortInfo_Port struct { + Port uint32 `protobuf:"varint,1,opt,name=port,proto3,oneof"` +} + +type PortInfo_Range_ struct { + Range *PortInfo_Range `protobuf:"bytes,2,opt,name=range,proto3,oneof"` +} + +func (*PortInfo_Port) isPortInfo_PortSelection() {} + +func (*PortInfo_Range_) isPortInfo_PortSelection() {} + +type ForwardingRule struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Protocol string `protobuf:"bytes,1,opt,name=protocol,proto3" json:"protocol,omitempty"` + DestinationPort *PortInfo `protobuf:"bytes,2,opt,name=destinationPort,proto3" json:"destinationPort,omitempty"` + TranslatedAddress string `protobuf:"bytes,3,opt,name=translatedAddress,proto3" json:"translatedAddress,omitempty"` + TranslatedHostname string `protobuf:"bytes,4,opt,name=translatedHostname,proto3" json:"translatedHostname,omitempty"` + TranslatedPort *PortInfo `protobuf:"bytes,5,opt,name=translatedPort,proto3" json:"translatedPort,omitempty"` +} + +func (x *ForwardingRule) Reset() { + *x = ForwardingRule{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForwardingRule) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForwardingRule) ProtoMessage() {} + +func (x *ForwardingRule) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ForwardingRule.ProtoReflect.Descriptor instead. +func (*ForwardingRule) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{27} +} + +func (x *ForwardingRule) GetProtocol() string { + if x != nil { + return x.Protocol + } + return "" +} + +func (x *ForwardingRule) GetDestinationPort() *PortInfo { + if x != nil { + return x.DestinationPort + } + return nil +} + +func (x *ForwardingRule) GetTranslatedAddress() string { + if x != nil { + return x.TranslatedAddress + } + return "" +} + +func (x *ForwardingRule) GetTranslatedHostname() string { + if x != nil { + return x.TranslatedHostname + } + return "" +} + +func (x *ForwardingRule) GetTranslatedPort() *PortInfo { + if x != nil { + return x.TranslatedPort + } + return nil +} + +type ForwardingRulesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Rules []*ForwardingRule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"` +} + +func (x *ForwardingRulesResponse) Reset() { + *x = ForwardingRulesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForwardingRulesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForwardingRulesResponse) ProtoMessage() {} + +func (x *ForwardingRulesResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ForwardingRulesResponse.ProtoReflect.Descriptor instead. +func (*ForwardingRulesResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{28} +} + +func (x *ForwardingRulesResponse) GetRules() []*ForwardingRule { + if x != nil { + return x.Rules + } + return nil +} + +// DebugBundler type DebugBundleRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2026,7 +2282,7 @@ type DebugBundleRequest struct { func (x *DebugBundleRequest) Reset() { *x = DebugBundleRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[25] + mi := &file_daemon_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2039,7 +2295,7 @@ func (x *DebugBundleRequest) String() string { func (*DebugBundleRequest) ProtoMessage() {} func (x *DebugBundleRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[25] + mi := &file_daemon_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2052,7 +2308,7 @@ func (x *DebugBundleRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugBundleRequest.ProtoReflect.Descriptor instead. func (*DebugBundleRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{25} + return file_daemon_proto_rawDescGZIP(), []int{29} } func (x *DebugBundleRequest) GetAnonymize() bool { @@ -2087,7 +2343,7 @@ type DebugBundleResponse struct { func (x *DebugBundleResponse) Reset() { *x = DebugBundleResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[26] + mi := &file_daemon_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2100,7 +2356,7 @@ func (x *DebugBundleResponse) String() string { func (*DebugBundleResponse) ProtoMessage() {} func (x *DebugBundleResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[26] + mi := &file_daemon_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2113,7 +2369,7 @@ func (x *DebugBundleResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugBundleResponse.ProtoReflect.Descriptor instead. func (*DebugBundleResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{26} + return file_daemon_proto_rawDescGZIP(), []int{30} } func (x *DebugBundleResponse) GetPath() string { @@ -2132,7 +2388,7 @@ type GetLogLevelRequest struct { func (x *GetLogLevelRequest) Reset() { *x = GetLogLevelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[27] + mi := &file_daemon_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2145,7 +2401,7 @@ func (x *GetLogLevelRequest) String() string { func (*GetLogLevelRequest) ProtoMessage() {} func (x *GetLogLevelRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[27] + mi := &file_daemon_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2158,7 +2414,7 @@ func (x *GetLogLevelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLogLevelRequest.ProtoReflect.Descriptor instead. func (*GetLogLevelRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{27} + return file_daemon_proto_rawDescGZIP(), []int{31} } type GetLogLevelResponse struct { @@ -2172,7 +2428,7 @@ type GetLogLevelResponse struct { func (x *GetLogLevelResponse) Reset() { *x = GetLogLevelResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[28] + mi := &file_daemon_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2185,7 +2441,7 @@ func (x *GetLogLevelResponse) String() string { func (*GetLogLevelResponse) ProtoMessage() {} func (x *GetLogLevelResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[28] + mi := &file_daemon_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2198,7 +2454,7 @@ func (x *GetLogLevelResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLogLevelResponse.ProtoReflect.Descriptor instead. func (*GetLogLevelResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{28} + return file_daemon_proto_rawDescGZIP(), []int{32} } func (x *GetLogLevelResponse) GetLevel() LogLevel { @@ -2219,7 +2475,7 @@ type SetLogLevelRequest struct { func (x *SetLogLevelRequest) Reset() { *x = SetLogLevelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[29] + mi := &file_daemon_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2232,7 +2488,7 @@ func (x *SetLogLevelRequest) String() string { func (*SetLogLevelRequest) ProtoMessage() {} func (x *SetLogLevelRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[29] + mi := &file_daemon_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2245,7 +2501,7 @@ func (x *SetLogLevelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetLogLevelRequest.ProtoReflect.Descriptor instead. func (*SetLogLevelRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{29} + return file_daemon_proto_rawDescGZIP(), []int{33} } func (x *SetLogLevelRequest) GetLevel() LogLevel { @@ -2264,7 +2520,7 @@ type SetLogLevelResponse struct { func (x *SetLogLevelResponse) Reset() { *x = SetLogLevelResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[30] + mi := &file_daemon_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2277,7 +2533,7 @@ func (x *SetLogLevelResponse) String() string { func (*SetLogLevelResponse) ProtoMessage() {} func (x *SetLogLevelResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[30] + mi := &file_daemon_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2290,7 +2546,7 @@ func (x *SetLogLevelResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetLogLevelResponse.ProtoReflect.Descriptor instead. func (*SetLogLevelResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{30} + return file_daemon_proto_rawDescGZIP(), []int{34} } // State represents a daemon state entry @@ -2305,7 +2561,7 @@ type State struct { func (x *State) Reset() { *x = State{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[31] + mi := &file_daemon_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2318,7 +2574,7 @@ func (x *State) String() string { func (*State) ProtoMessage() {} func (x *State) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[31] + mi := &file_daemon_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2331,7 +2587,7 @@ func (x *State) ProtoReflect() protoreflect.Message { // Deprecated: Use State.ProtoReflect.Descriptor instead. func (*State) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{31} + return file_daemon_proto_rawDescGZIP(), []int{35} } func (x *State) GetName() string { @@ -2351,7 +2607,7 @@ type ListStatesRequest struct { func (x *ListStatesRequest) Reset() { *x = ListStatesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[32] + mi := &file_daemon_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2364,7 +2620,7 @@ func (x *ListStatesRequest) String() string { func (*ListStatesRequest) ProtoMessage() {} func (x *ListStatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[32] + mi := &file_daemon_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2377,7 +2633,7 @@ func (x *ListStatesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListStatesRequest.ProtoReflect.Descriptor instead. func (*ListStatesRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{32} + return file_daemon_proto_rawDescGZIP(), []int{36} } // ListStatesResponse contains a list of states @@ -2392,7 +2648,7 @@ type ListStatesResponse struct { func (x *ListStatesResponse) Reset() { *x = ListStatesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[33] + mi := &file_daemon_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2405,7 +2661,7 @@ func (x *ListStatesResponse) String() string { func (*ListStatesResponse) ProtoMessage() {} func (x *ListStatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[33] + mi := &file_daemon_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2418,7 +2674,7 @@ func (x *ListStatesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListStatesResponse.ProtoReflect.Descriptor instead. func (*ListStatesResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{33} + return file_daemon_proto_rawDescGZIP(), []int{37} } func (x *ListStatesResponse) GetStates() []*State { @@ -2441,7 +2697,7 @@ type CleanStateRequest struct { func (x *CleanStateRequest) Reset() { *x = CleanStateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[34] + mi := &file_daemon_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2454,7 +2710,7 @@ func (x *CleanStateRequest) String() string { func (*CleanStateRequest) ProtoMessage() {} func (x *CleanStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[34] + mi := &file_daemon_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2467,7 +2723,7 @@ func (x *CleanStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CleanStateRequest.ProtoReflect.Descriptor instead. func (*CleanStateRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{34} + return file_daemon_proto_rawDescGZIP(), []int{38} } func (x *CleanStateRequest) GetStateName() string { @@ -2496,7 +2752,7 @@ type CleanStateResponse struct { func (x *CleanStateResponse) Reset() { *x = CleanStateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[35] + mi := &file_daemon_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2509,7 +2765,7 @@ func (x *CleanStateResponse) String() string { func (*CleanStateResponse) ProtoMessage() {} func (x *CleanStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[35] + mi := &file_daemon_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2522,7 +2778,7 @@ func (x *CleanStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CleanStateResponse.ProtoReflect.Descriptor instead. func (*CleanStateResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{35} + return file_daemon_proto_rawDescGZIP(), []int{39} } func (x *CleanStateResponse) GetCleanedStates() int32 { @@ -2545,7 +2801,7 @@ type DeleteStateRequest struct { func (x *DeleteStateRequest) Reset() { *x = DeleteStateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[36] + mi := &file_daemon_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2558,7 +2814,7 @@ func (x *DeleteStateRequest) String() string { func (*DeleteStateRequest) ProtoMessage() {} func (x *DeleteStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[36] + mi := &file_daemon_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2571,7 +2827,7 @@ func (x *DeleteStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteStateRequest.ProtoReflect.Descriptor instead. func (*DeleteStateRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{36} + return file_daemon_proto_rawDescGZIP(), []int{40} } func (x *DeleteStateRequest) GetStateName() string { @@ -2600,7 +2856,7 @@ type DeleteStateResponse struct { func (x *DeleteStateResponse) Reset() { *x = DeleteStateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[37] + mi := &file_daemon_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2613,7 +2869,7 @@ func (x *DeleteStateResponse) String() string { func (*DeleteStateResponse) ProtoMessage() {} func (x *DeleteStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[37] + mi := &file_daemon_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2626,7 +2882,7 @@ func (x *DeleteStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteStateResponse.ProtoReflect.Descriptor instead. func (*DeleteStateResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{37} + return file_daemon_proto_rawDescGZIP(), []int{41} } func (x *DeleteStateResponse) GetDeletedStates() int32 { @@ -2647,7 +2903,7 @@ type SetNetworkMapPersistenceRequest struct { func (x *SetNetworkMapPersistenceRequest) Reset() { *x = SetNetworkMapPersistenceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[38] + mi := &file_daemon_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2660,7 +2916,7 @@ func (x *SetNetworkMapPersistenceRequest) String() string { func (*SetNetworkMapPersistenceRequest) ProtoMessage() {} func (x *SetNetworkMapPersistenceRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[38] + mi := &file_daemon_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2673,7 +2929,7 @@ func (x *SetNetworkMapPersistenceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetNetworkMapPersistenceRequest.ProtoReflect.Descriptor instead. func (*SetNetworkMapPersistenceRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{38} + return file_daemon_proto_rawDescGZIP(), []int{42} } func (x *SetNetworkMapPersistenceRequest) GetEnabled() bool { @@ -2692,7 +2948,7 @@ type SetNetworkMapPersistenceResponse struct { func (x *SetNetworkMapPersistenceResponse) Reset() { *x = SetNetworkMapPersistenceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[39] + mi := &file_daemon_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2705,7 +2961,7 @@ func (x *SetNetworkMapPersistenceResponse) String() string { func (*SetNetworkMapPersistenceResponse) ProtoMessage() {} func (x *SetNetworkMapPersistenceResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[39] + mi := &file_daemon_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2718,7 +2974,7 @@ func (x *SetNetworkMapPersistenceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetNetworkMapPersistenceResponse.ProtoReflect.Descriptor instead. func (*SetNetworkMapPersistenceResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{39} + return file_daemon_proto_rawDescGZIP(), []int{43} } type TCPFlags struct { @@ -2737,7 +2993,7 @@ type TCPFlags struct { func (x *TCPFlags) Reset() { *x = TCPFlags{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[40] + mi := &file_daemon_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2750,7 +3006,7 @@ func (x *TCPFlags) String() string { func (*TCPFlags) ProtoMessage() {} func (x *TCPFlags) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[40] + mi := &file_daemon_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2763,7 +3019,7 @@ func (x *TCPFlags) ProtoReflect() protoreflect.Message { // Deprecated: Use TCPFlags.ProtoReflect.Descriptor instead. func (*TCPFlags) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{40} + return file_daemon_proto_rawDescGZIP(), []int{44} } func (x *TCPFlags) GetSyn() bool { @@ -2827,7 +3083,7 @@ type TracePacketRequest struct { func (x *TracePacketRequest) Reset() { *x = TracePacketRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[41] + mi := &file_daemon_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2840,7 +3096,7 @@ func (x *TracePacketRequest) String() string { func (*TracePacketRequest) ProtoMessage() {} func (x *TracePacketRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[41] + mi := &file_daemon_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2853,7 +3109,7 @@ func (x *TracePacketRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TracePacketRequest.ProtoReflect.Descriptor instead. func (*TracePacketRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{41} + return file_daemon_proto_rawDescGZIP(), []int{45} } func (x *TracePacketRequest) GetSourceIp() string { @@ -2933,7 +3189,7 @@ type TraceStage struct { func (x *TraceStage) Reset() { *x = TraceStage{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[42] + mi := &file_daemon_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2946,7 +3202,7 @@ func (x *TraceStage) String() string { func (*TraceStage) ProtoMessage() {} func (x *TraceStage) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[42] + mi := &file_daemon_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2959,7 +3215,7 @@ func (x *TraceStage) ProtoReflect() protoreflect.Message { // Deprecated: Use TraceStage.ProtoReflect.Descriptor instead. func (*TraceStage) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{42} + return file_daemon_proto_rawDescGZIP(), []int{46} } func (x *TraceStage) GetName() string { @@ -3002,7 +3258,7 @@ type TracePacketResponse struct { func (x *TracePacketResponse) Reset() { *x = TracePacketResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[43] + mi := &file_daemon_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3015,7 +3271,7 @@ func (x *TracePacketResponse) String() string { func (*TracePacketResponse) ProtoMessage() {} func (x *TracePacketResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[43] + mi := &file_daemon_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3028,7 +3284,7 @@ func (x *TracePacketResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TracePacketResponse.ProtoReflect.Descriptor instead. func (*TracePacketResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{43} + return file_daemon_proto_rawDescGZIP(), []int{47} } func (x *TracePacketResponse) GetStages() []*TraceStage { @@ -3054,7 +3310,7 @@ type SubscribeRequest struct { func (x *SubscribeRequest) Reset() { *x = SubscribeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[44] + mi := &file_daemon_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3067,7 +3323,7 @@ func (x *SubscribeRequest) String() string { func (*SubscribeRequest) ProtoMessage() {} func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[44] + mi := &file_daemon_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3080,7 +3336,7 @@ func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead. func (*SubscribeRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{44} + return file_daemon_proto_rawDescGZIP(), []int{48} } type SystemEvent struct { @@ -3100,7 +3356,7 @@ type SystemEvent struct { func (x *SystemEvent) Reset() { *x = SystemEvent{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[45] + mi := &file_daemon_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3113,7 +3369,7 @@ func (x *SystemEvent) String() string { func (*SystemEvent) ProtoMessage() {} func (x *SystemEvent) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[45] + mi := &file_daemon_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3126,7 +3382,7 @@ func (x *SystemEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemEvent.ProtoReflect.Descriptor instead. func (*SystemEvent) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{45} + return file_daemon_proto_rawDescGZIP(), []int{49} } func (x *SystemEvent) GetId() string { @@ -3187,7 +3443,7 @@ type GetEventsRequest struct { func (x *GetEventsRequest) Reset() { *x = GetEventsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[46] + mi := &file_daemon_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3200,7 +3456,7 @@ func (x *GetEventsRequest) String() string { func (*GetEventsRequest) ProtoMessage() {} func (x *GetEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[46] + mi := &file_daemon_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3213,7 +3469,7 @@ func (x *GetEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetEventsRequest.ProtoReflect.Descriptor instead. func (*GetEventsRequest) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{46} + return file_daemon_proto_rawDescGZIP(), []int{50} } type GetEventsResponse struct { @@ -3227,7 +3483,7 @@ type GetEventsResponse struct { func (x *GetEventsResponse) Reset() { *x = GetEventsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[47] + mi := &file_daemon_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3240,7 +3496,7 @@ func (x *GetEventsResponse) String() string { func (*GetEventsResponse) ProtoMessage() {} func (x *GetEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_daemon_proto_msgTypes[47] + mi := &file_daemon_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3253,7 +3509,7 @@ func (x *GetEventsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetEventsResponse.ProtoReflect.Descriptor instead. func (*GetEventsResponse) Descriptor() ([]byte, []int) { - return file_daemon_proto_rawDescGZIP(), []int{47} + return file_daemon_proto_rawDescGZIP(), []int{51} } func (x *GetEventsResponse) GetEvents() []*SystemEvent { @@ -3263,6 +3519,61 @@ func (x *GetEventsResponse) GetEvents() []*SystemEvent { return nil } +type PortInfo_Range struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Start uint32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` + End uint32 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *PortInfo_Range) Reset() { + *x = PortInfo_Range{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PortInfo_Range) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PortInfo_Range) ProtoMessage() {} + +func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[53] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PortInfo_Range.ProtoReflect.Descriptor instead. +func (*PortInfo_Range) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{26, 0} +} + +func (x *PortInfo_Range) GetStart() uint32 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *PortInfo_Range) GetEnd() uint32 { + if x != nil { + return x.End + } + return 0 +} + var File_daemon_proto protoreflect.FileDescriptor var file_daemon_proto_rawDesc = []byte{ @@ -3273,7 +3584,8 @@ var file_daemon_proto_rawDesc = []byte{ 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb0, 0x0c, 0x0a, 0x0c, 0x4c, 0x6f, + 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0e, 0x0a, 0x0c, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb0, 0x0c, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, @@ -3523,7 +3835,7 @@ var file_daemon_proto_rawDesc = []byte{ 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xff, 0x02, 0x0a, 0x0a, 0x46, 0x75, 0x6c, + 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xb9, 0x03, 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, @@ -3544,273 +3856,312 @@ var file_daemon_proto_rawDesc = []byte{ 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x53, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x0a, 0x64, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, - 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, - 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x3f, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x64, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x73, 0x22, 0x61, 0x0a, 0x15, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, - 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x70, 0x70, - 0x65, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x18, 0x0a, 0x16, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1a, 0x0a, 0x06, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x70, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x70, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x07, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, - 0x50, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x64, 0x49, 0x50, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x1a, 0x4e, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6a, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, - 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, - 0x09, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, - 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, - 0x6e, 0x66, 0x6f, 0x22, 0x29, 0x0a, 0x13, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x14, - 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x3d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, - 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x22, 0x3c, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, - 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, - 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x44, 0x0a, 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3b, 0x0a, - 0x12, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x6c, 0x65, - 0x61, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, - 0x6c, 0x22, 0x3c, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, - 0x3b, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, - 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x20, - 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, - 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x76, 0x0a, 0x08, 0x54, 0x43, 0x50, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, - 0x73, 0x79, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x73, 0x79, 0x6e, 0x12, 0x10, - 0x0a, 0x03, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x63, 0x6b, - 0x12, 0x10, 0x0a, 0x03, 0x66, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x66, - 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x03, 0x72, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x03, 0x70, 0x73, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x67, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x03, 0x75, 0x72, 0x67, 0x22, 0x80, 0x03, 0x0a, 0x12, 0x54, 0x72, 0x61, - 0x63, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x70, 0x12, 0x25, 0x0a, 0x0e, - 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, - 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, - 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x70, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x09, 0x74, 0x63, 0x70, - 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x54, 0x43, 0x50, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x48, 0x00, - 0x52, 0x08, 0x74, 0x63, 0x70, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, - 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, - 0x48, 0x01, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, - 0x20, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0d, 0x48, 0x02, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, - 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x42, - 0x0c, 0x0a, 0x0a, 0x5f, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x0c, 0x0a, - 0x0a, 0x5f, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x0a, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x12, 0x32, 0x0a, 0x12, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, - 0x52, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, - 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x6e, 0x0a, - 0x13, 0x54, 0x72, 0x61, 0x63, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x54, 0x72, - 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, - 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6e, - 0x61, 0x6c, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x12, 0x0a, - 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x93, 0x04, 0x0a, 0x0b, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x38, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, - 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x38, 0x0a, 0x08, 0x63, - 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, - 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x08, 0x63, 0x61, 0x74, - 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x20, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3d, 0x0a, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, - 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3a, 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, - 0x69, 0x74, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x0b, 0x0a, - 0x07, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, - 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, - 0x4c, 0x10, 0x03, 0x22, 0x52, 0x0a, 0x08, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, - 0x0b, 0x0a, 0x07, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, - 0x44, 0x4e, 0x53, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, - 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4f, 0x4e, - 0x4e, 0x45, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x53, - 0x59, 0x53, 0x54, 0x45, 0x4d, 0x10, 0x04, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x40, 0x0a, 0x11, 0x47, - 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x2b, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2a, 0x62, 0x0a, - 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, 0x49, 0x43, 0x10, - 0x01, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, - 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, - 0x04, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x44, - 0x45, 0x42, 0x55, 0x47, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, - 0x07, 0x32, 0xe7, 0x0a, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, - 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x4c, - 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0e, 0x53, 0x65, 0x6c, 0x65, + 0x65, 0x52, 0x0a, 0x64, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, + 0x17, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3f, 0x0a, 0x14, 0x4c, + 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x61, 0x0a, 0x15, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x49, 0x44, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, + 0x18, 0x0a, 0x16, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a, 0x06, 0x49, 0x50, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x03, 0x69, 0x70, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, + 0x44, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x42, 0x0a, + 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, + 0x73, 0x1a, 0x4e, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x92, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, + 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x04, + 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x6f, 0x72, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x1a, 0x2f, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x65, 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x80, 0x02, 0x0a, 0x0e, 0x46, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x72, + 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x2e, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x38, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x47, 0x0a, 0x17, 0x46, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, + 0x65, 0x73, 0x22, 0x6a, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6e, 0x6f, 0x6e, + 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x6e, 0x6f, + 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, + 0x0a, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x29, + 0x0a, 0x13, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x3d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, + 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x3c, + 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x15, 0x0a, 0x13, + 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x22, 0x44, 0x0a, 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3b, 0x0a, 0x12, 0x43, 0x6c, 0x65, 0x61, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3c, 0x0a, 0x13, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x64, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x1f, 0x53, 0x65, + 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, 0x0a, 0x08, 0x54, + 0x43, 0x50, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x79, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x73, 0x79, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x6b, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x66, + 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x66, 0x69, 0x6e, 0x12, 0x10, 0x0a, + 0x03, 0x72, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x72, 0x73, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x70, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x70, 0x73, + 0x68, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, + 0x75, 0x72, 0x67, 0x22, 0x80, 0x03, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x63, 0x65, 0x50, 0x61, 0x63, + 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x70, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x66, 0x6c, 0x61, 0x67, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x54, 0x43, 0x50, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x48, 0x00, 0x52, 0x08, 0x74, 0x63, 0x70, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x08, 0x69, + 0x63, 0x6d, 0x70, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x69, 0x63, + 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, + 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x69, + 0x63, 0x6d, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x69, 0x63, 0x6d, + 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, + 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x32, 0x0a, + 0x12, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x66, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x88, 0x01, + 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x6e, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x63, + 0x65, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, + 0x61, 0x67, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x66, + 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x69, 0x73, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x93, 0x04, 0x0a, + 0x0b, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x08, + 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, + 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x38, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, + 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x75, 0x73, + 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x75, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3d, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x3a, 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x08, + 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x52, 0x4e, + 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, + 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x03, 0x22, 0x52, + 0x0a, 0x08, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x45, + 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x4e, 0x53, 0x10, 0x01, + 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, + 0x56, 0x49, 0x54, 0x59, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, + 0x10, 0x04, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x40, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2a, 0x62, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, 0x49, 0x43, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, + 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, + 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, + 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, + 0x06, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x07, 0x32, 0xb3, 0x0b, 0x0a, + 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, + 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, + 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, + 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, + 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, + 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x10, 0x44, 0x65, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x10, 0x44, - 0x65, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, - 0x1d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, - 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, - 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, - 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, - 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, - 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, - 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x65, - 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, - 0x63, 0x65, 0x12, 0x27, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0f, 0x46, + 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x14, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, 0x75, 0x67, + 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, + 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x53, + 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, + 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, + 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, + 0x18, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, + 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, + 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, - 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x63, 0x65, - 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x54, 0x72, 0x61, 0x63, - 0x65, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x44, 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, + 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1a, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x50, 0x61, 0x63, 0x6b, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3826,133 +4177,144 @@ func file_daemon_proto_rawDescGZIP() []byte { } var file_daemon_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 50) +var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 55) var file_daemon_proto_goTypes = []interface{}{ (LogLevel)(0), // 0: daemon.LogLevel (SystemEvent_Severity)(0), // 1: daemon.SystemEvent.Severity (SystemEvent_Category)(0), // 2: daemon.SystemEvent.Category - (*LoginRequest)(nil), // 3: daemon.LoginRequest - (*LoginResponse)(nil), // 4: daemon.LoginResponse - (*WaitSSOLoginRequest)(nil), // 5: daemon.WaitSSOLoginRequest - (*WaitSSOLoginResponse)(nil), // 6: daemon.WaitSSOLoginResponse - (*UpRequest)(nil), // 7: daemon.UpRequest - (*UpResponse)(nil), // 8: daemon.UpResponse - (*StatusRequest)(nil), // 9: daemon.StatusRequest - (*StatusResponse)(nil), // 10: daemon.StatusResponse - (*DownRequest)(nil), // 11: daemon.DownRequest - (*DownResponse)(nil), // 12: daemon.DownResponse - (*GetConfigRequest)(nil), // 13: daemon.GetConfigRequest - (*GetConfigResponse)(nil), // 14: daemon.GetConfigResponse - (*PeerState)(nil), // 15: daemon.PeerState - (*LocalPeerState)(nil), // 16: daemon.LocalPeerState - (*SignalState)(nil), // 17: daemon.SignalState - (*ManagementState)(nil), // 18: daemon.ManagementState - (*RelayState)(nil), // 19: daemon.RelayState - (*NSGroupState)(nil), // 20: daemon.NSGroupState - (*FullStatus)(nil), // 21: daemon.FullStatus - (*ListNetworksRequest)(nil), // 22: daemon.ListNetworksRequest - (*ListNetworksResponse)(nil), // 23: daemon.ListNetworksResponse - (*SelectNetworksRequest)(nil), // 24: daemon.SelectNetworksRequest - (*SelectNetworksResponse)(nil), // 25: daemon.SelectNetworksResponse - (*IPList)(nil), // 26: daemon.IPList - (*Network)(nil), // 27: daemon.Network - (*DebugBundleRequest)(nil), // 28: daemon.DebugBundleRequest - (*DebugBundleResponse)(nil), // 29: daemon.DebugBundleResponse - (*GetLogLevelRequest)(nil), // 30: daemon.GetLogLevelRequest - (*GetLogLevelResponse)(nil), // 31: daemon.GetLogLevelResponse - (*SetLogLevelRequest)(nil), // 32: daemon.SetLogLevelRequest - (*SetLogLevelResponse)(nil), // 33: daemon.SetLogLevelResponse - (*State)(nil), // 34: daemon.State - (*ListStatesRequest)(nil), // 35: daemon.ListStatesRequest - (*ListStatesResponse)(nil), // 36: daemon.ListStatesResponse - (*CleanStateRequest)(nil), // 37: daemon.CleanStateRequest - (*CleanStateResponse)(nil), // 38: daemon.CleanStateResponse - (*DeleteStateRequest)(nil), // 39: daemon.DeleteStateRequest - (*DeleteStateResponse)(nil), // 40: daemon.DeleteStateResponse - (*SetNetworkMapPersistenceRequest)(nil), // 41: daemon.SetNetworkMapPersistenceRequest - (*SetNetworkMapPersistenceResponse)(nil), // 42: daemon.SetNetworkMapPersistenceResponse - (*TCPFlags)(nil), // 43: daemon.TCPFlags - (*TracePacketRequest)(nil), // 44: daemon.TracePacketRequest - (*TraceStage)(nil), // 45: daemon.TraceStage - (*TracePacketResponse)(nil), // 46: daemon.TracePacketResponse - (*SubscribeRequest)(nil), // 47: daemon.SubscribeRequest - (*SystemEvent)(nil), // 48: daemon.SystemEvent - (*GetEventsRequest)(nil), // 49: daemon.GetEventsRequest - (*GetEventsResponse)(nil), // 50: daemon.GetEventsResponse - nil, // 51: daemon.Network.ResolvedIPsEntry - nil, // 52: daemon.SystemEvent.MetadataEntry - (*durationpb.Duration)(nil), // 53: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 54: google.protobuf.Timestamp + (*EmptyRequest)(nil), // 3: daemon.EmptyRequest + (*LoginRequest)(nil), // 4: daemon.LoginRequest + (*LoginResponse)(nil), // 5: daemon.LoginResponse + (*WaitSSOLoginRequest)(nil), // 6: daemon.WaitSSOLoginRequest + (*WaitSSOLoginResponse)(nil), // 7: daemon.WaitSSOLoginResponse + (*UpRequest)(nil), // 8: daemon.UpRequest + (*UpResponse)(nil), // 9: daemon.UpResponse + (*StatusRequest)(nil), // 10: daemon.StatusRequest + (*StatusResponse)(nil), // 11: daemon.StatusResponse + (*DownRequest)(nil), // 12: daemon.DownRequest + (*DownResponse)(nil), // 13: daemon.DownResponse + (*GetConfigRequest)(nil), // 14: daemon.GetConfigRequest + (*GetConfigResponse)(nil), // 15: daemon.GetConfigResponse + (*PeerState)(nil), // 16: daemon.PeerState + (*LocalPeerState)(nil), // 17: daemon.LocalPeerState + (*SignalState)(nil), // 18: daemon.SignalState + (*ManagementState)(nil), // 19: daemon.ManagementState + (*RelayState)(nil), // 20: daemon.RelayState + (*NSGroupState)(nil), // 21: daemon.NSGroupState + (*FullStatus)(nil), // 22: daemon.FullStatus + (*ListNetworksRequest)(nil), // 23: daemon.ListNetworksRequest + (*ListNetworksResponse)(nil), // 24: daemon.ListNetworksResponse + (*SelectNetworksRequest)(nil), // 25: daemon.SelectNetworksRequest + (*SelectNetworksResponse)(nil), // 26: daemon.SelectNetworksResponse + (*IPList)(nil), // 27: daemon.IPList + (*Network)(nil), // 28: daemon.Network + (*PortInfo)(nil), // 29: daemon.PortInfo + (*ForwardingRule)(nil), // 30: daemon.ForwardingRule + (*ForwardingRulesResponse)(nil), // 31: daemon.ForwardingRulesResponse + (*DebugBundleRequest)(nil), // 32: daemon.DebugBundleRequest + (*DebugBundleResponse)(nil), // 33: daemon.DebugBundleResponse + (*GetLogLevelRequest)(nil), // 34: daemon.GetLogLevelRequest + (*GetLogLevelResponse)(nil), // 35: daemon.GetLogLevelResponse + (*SetLogLevelRequest)(nil), // 36: daemon.SetLogLevelRequest + (*SetLogLevelResponse)(nil), // 37: daemon.SetLogLevelResponse + (*State)(nil), // 38: daemon.State + (*ListStatesRequest)(nil), // 39: daemon.ListStatesRequest + (*ListStatesResponse)(nil), // 40: daemon.ListStatesResponse + (*CleanStateRequest)(nil), // 41: daemon.CleanStateRequest + (*CleanStateResponse)(nil), // 42: daemon.CleanStateResponse + (*DeleteStateRequest)(nil), // 43: daemon.DeleteStateRequest + (*DeleteStateResponse)(nil), // 44: daemon.DeleteStateResponse + (*SetNetworkMapPersistenceRequest)(nil), // 45: daemon.SetNetworkMapPersistenceRequest + (*SetNetworkMapPersistenceResponse)(nil), // 46: daemon.SetNetworkMapPersistenceResponse + (*TCPFlags)(nil), // 47: daemon.TCPFlags + (*TracePacketRequest)(nil), // 48: daemon.TracePacketRequest + (*TraceStage)(nil), // 49: daemon.TraceStage + (*TracePacketResponse)(nil), // 50: daemon.TracePacketResponse + (*SubscribeRequest)(nil), // 51: daemon.SubscribeRequest + (*SystemEvent)(nil), // 52: daemon.SystemEvent + (*GetEventsRequest)(nil), // 53: daemon.GetEventsRequest + (*GetEventsResponse)(nil), // 54: daemon.GetEventsResponse + nil, // 55: daemon.Network.ResolvedIPsEntry + (*PortInfo_Range)(nil), // 56: daemon.PortInfo.Range + nil, // 57: daemon.SystemEvent.MetadataEntry + (*durationpb.Duration)(nil), // 58: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 59: google.protobuf.Timestamp } var file_daemon_proto_depIdxs = []int32{ - 53, // 0: daemon.LoginRequest.dnsRouteInterval:type_name -> google.protobuf.Duration - 21, // 1: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus - 54, // 2: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp - 54, // 3: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp - 53, // 4: daemon.PeerState.latency:type_name -> google.protobuf.Duration - 18, // 5: daemon.FullStatus.managementState:type_name -> daemon.ManagementState - 17, // 6: daemon.FullStatus.signalState:type_name -> daemon.SignalState - 16, // 7: daemon.FullStatus.localPeerState:type_name -> daemon.LocalPeerState - 15, // 8: daemon.FullStatus.peers:type_name -> daemon.PeerState - 19, // 9: daemon.FullStatus.relays:type_name -> daemon.RelayState - 20, // 10: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState - 48, // 11: daemon.FullStatus.events:type_name -> daemon.SystemEvent - 27, // 12: daemon.ListNetworksResponse.routes:type_name -> daemon.Network - 51, // 13: daemon.Network.resolvedIPs:type_name -> daemon.Network.ResolvedIPsEntry - 0, // 14: daemon.GetLogLevelResponse.level:type_name -> daemon.LogLevel - 0, // 15: daemon.SetLogLevelRequest.level:type_name -> daemon.LogLevel - 34, // 16: daemon.ListStatesResponse.states:type_name -> daemon.State - 43, // 17: daemon.TracePacketRequest.tcp_flags:type_name -> daemon.TCPFlags - 45, // 18: daemon.TracePacketResponse.stages:type_name -> daemon.TraceStage - 1, // 19: daemon.SystemEvent.severity:type_name -> daemon.SystemEvent.Severity - 2, // 20: daemon.SystemEvent.category:type_name -> daemon.SystemEvent.Category - 54, // 21: daemon.SystemEvent.timestamp:type_name -> google.protobuf.Timestamp - 52, // 22: daemon.SystemEvent.metadata:type_name -> daemon.SystemEvent.MetadataEntry - 48, // 23: daemon.GetEventsResponse.events:type_name -> daemon.SystemEvent - 26, // 24: daemon.Network.ResolvedIPsEntry.value:type_name -> daemon.IPList - 3, // 25: daemon.DaemonService.Login:input_type -> daemon.LoginRequest - 5, // 26: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest - 7, // 27: daemon.DaemonService.Up:input_type -> daemon.UpRequest - 9, // 28: daemon.DaemonService.Status:input_type -> daemon.StatusRequest - 11, // 29: daemon.DaemonService.Down:input_type -> daemon.DownRequest - 13, // 30: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest - 22, // 31: daemon.DaemonService.ListNetworks:input_type -> daemon.ListNetworksRequest - 24, // 32: daemon.DaemonService.SelectNetworks:input_type -> daemon.SelectNetworksRequest - 24, // 33: daemon.DaemonService.DeselectNetworks:input_type -> daemon.SelectNetworksRequest - 28, // 34: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest - 30, // 35: daemon.DaemonService.GetLogLevel:input_type -> daemon.GetLogLevelRequest - 32, // 36: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest - 35, // 37: daemon.DaemonService.ListStates:input_type -> daemon.ListStatesRequest - 37, // 38: daemon.DaemonService.CleanState:input_type -> daemon.CleanStateRequest - 39, // 39: daemon.DaemonService.DeleteState:input_type -> daemon.DeleteStateRequest - 41, // 40: daemon.DaemonService.SetNetworkMapPersistence:input_type -> daemon.SetNetworkMapPersistenceRequest - 44, // 41: daemon.DaemonService.TracePacket:input_type -> daemon.TracePacketRequest - 47, // 42: daemon.DaemonService.SubscribeEvents:input_type -> daemon.SubscribeRequest - 49, // 43: daemon.DaemonService.GetEvents:input_type -> daemon.GetEventsRequest - 4, // 44: daemon.DaemonService.Login:output_type -> daemon.LoginResponse - 6, // 45: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse - 8, // 46: daemon.DaemonService.Up:output_type -> daemon.UpResponse - 10, // 47: daemon.DaemonService.Status:output_type -> daemon.StatusResponse - 12, // 48: daemon.DaemonService.Down:output_type -> daemon.DownResponse - 14, // 49: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse - 23, // 50: daemon.DaemonService.ListNetworks:output_type -> daemon.ListNetworksResponse - 25, // 51: daemon.DaemonService.SelectNetworks:output_type -> daemon.SelectNetworksResponse - 25, // 52: daemon.DaemonService.DeselectNetworks:output_type -> daemon.SelectNetworksResponse - 29, // 53: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse - 31, // 54: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse - 33, // 55: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse - 36, // 56: daemon.DaemonService.ListStates:output_type -> daemon.ListStatesResponse - 38, // 57: daemon.DaemonService.CleanState:output_type -> daemon.CleanStateResponse - 40, // 58: daemon.DaemonService.DeleteState:output_type -> daemon.DeleteStateResponse - 42, // 59: daemon.DaemonService.SetNetworkMapPersistence:output_type -> daemon.SetNetworkMapPersistenceResponse - 46, // 60: daemon.DaemonService.TracePacket:output_type -> daemon.TracePacketResponse - 48, // 61: daemon.DaemonService.SubscribeEvents:output_type -> daemon.SystemEvent - 50, // 62: daemon.DaemonService.GetEvents:output_type -> daemon.GetEventsResponse - 44, // [44:63] is the sub-list for method output_type - 25, // [25:44] is the sub-list for method input_type - 25, // [25:25] is the sub-list for extension type_name - 25, // [25:25] is the sub-list for extension extendee - 0, // [0:25] is the sub-list for field type_name + 58, // 0: daemon.LoginRequest.dnsRouteInterval:type_name -> google.protobuf.Duration + 22, // 1: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus + 59, // 2: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp + 59, // 3: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp + 58, // 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 + 16, // 8: daemon.FullStatus.peers:type_name -> daemon.PeerState + 20, // 9: daemon.FullStatus.relays:type_name -> daemon.RelayState + 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 + 55, // 13: daemon.Network.resolvedIPs:type_name -> daemon.Network.ResolvedIPsEntry + 56, // 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 + 0, // 18: daemon.GetLogLevelResponse.level:type_name -> daemon.LogLevel + 0, // 19: daemon.SetLogLevelRequest.level:type_name -> daemon.LogLevel + 38, // 20: daemon.ListStatesResponse.states:type_name -> daemon.State + 47, // 21: daemon.TracePacketRequest.tcp_flags:type_name -> daemon.TCPFlags + 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 + 59, // 25: daemon.SystemEvent.timestamp:type_name -> google.protobuf.Timestamp + 57, // 26: daemon.SystemEvent.metadata:type_name -> daemon.SystemEvent.MetadataEntry + 52, // 27: daemon.GetEventsResponse.events:type_name -> daemon.SystemEvent + 27, // 28: daemon.Network.ResolvedIPsEntry.value:type_name -> daemon.IPList + 4, // 29: daemon.DaemonService.Login:input_type -> daemon.LoginRequest + 6, // 30: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest + 8, // 31: daemon.DaemonService.Up:input_type -> daemon.UpRequest + 10, // 32: daemon.DaemonService.Status:input_type -> daemon.StatusRequest + 12, // 33: daemon.DaemonService.Down:input_type -> daemon.DownRequest + 14, // 34: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest + 23, // 35: daemon.DaemonService.ListNetworks:input_type -> daemon.ListNetworksRequest + 25, // 36: daemon.DaemonService.SelectNetworks:input_type -> daemon.SelectNetworksRequest + 25, // 37: daemon.DaemonService.DeselectNetworks:input_type -> daemon.SelectNetworksRequest + 3, // 38: daemon.DaemonService.ForwardingRules:input_type -> daemon.EmptyRequest + 32, // 39: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest + 34, // 40: daemon.DaemonService.GetLogLevel:input_type -> daemon.GetLogLevelRequest + 36, // 41: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest + 39, // 42: daemon.DaemonService.ListStates:input_type -> daemon.ListStatesRequest + 41, // 43: daemon.DaemonService.CleanState:input_type -> daemon.CleanStateRequest + 43, // 44: daemon.DaemonService.DeleteState:input_type -> daemon.DeleteStateRequest + 45, // 45: daemon.DaemonService.SetNetworkMapPersistence:input_type -> daemon.SetNetworkMapPersistenceRequest + 48, // 46: daemon.DaemonService.TracePacket:input_type -> daemon.TracePacketRequest + 51, // 47: daemon.DaemonService.SubscribeEvents:input_type -> daemon.SubscribeRequest + 53, // 48: daemon.DaemonService.GetEvents:input_type -> daemon.GetEventsRequest + 5, // 49: daemon.DaemonService.Login:output_type -> daemon.LoginResponse + 7, // 50: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse + 9, // 51: daemon.DaemonService.Up:output_type -> daemon.UpResponse + 11, // 52: daemon.DaemonService.Status:output_type -> daemon.StatusResponse + 13, // 53: daemon.DaemonService.Down:output_type -> daemon.DownResponse + 15, // 54: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse + 24, // 55: daemon.DaemonService.ListNetworks:output_type -> daemon.ListNetworksResponse + 26, // 56: daemon.DaemonService.SelectNetworks:output_type -> daemon.SelectNetworksResponse + 26, // 57: daemon.DaemonService.DeselectNetworks:output_type -> daemon.SelectNetworksResponse + 31, // 58: daemon.DaemonService.ForwardingRules:output_type -> daemon.ForwardingRulesResponse + 33, // 59: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse + 35, // 60: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse + 37, // 61: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse + 40, // 62: daemon.DaemonService.ListStates:output_type -> daemon.ListStatesResponse + 42, // 63: daemon.DaemonService.CleanState:output_type -> daemon.CleanStateResponse + 44, // 64: daemon.DaemonService.DeleteState:output_type -> daemon.DeleteStateResponse + 46, // 65: daemon.DaemonService.SetNetworkMapPersistence:output_type -> daemon.SetNetworkMapPersistenceResponse + 50, // 66: daemon.DaemonService.TracePacket:output_type -> daemon.TracePacketResponse + 52, // 67: daemon.DaemonService.SubscribeEvents:output_type -> daemon.SystemEvent + 54, // 68: daemon.DaemonService.GetEvents:output_type -> daemon.GetEventsResponse + 49, // [49:69] is the sub-list for method output_type + 29, // [29:49] is the sub-list for method input_type + 29, // [29:29] is the sub-list for extension type_name + 29, // [29:29] is the sub-list for extension extendee + 0, // [0:29] is the sub-list for field type_name } func init() { file_daemon_proto_init() } @@ -3962,7 +4324,7 @@ func file_daemon_proto_init() { } if !protoimpl.UnsafeEnabled { file_daemon_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginRequest); i { + switch v := v.(*EmptyRequest); i { case 0: return &v.state case 1: @@ -3974,7 +4336,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginResponse); i { + switch v := v.(*LoginRequest); i { case 0: return &v.state case 1: @@ -3986,7 +4348,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WaitSSOLoginRequest); i { + switch v := v.(*LoginResponse); i { case 0: return &v.state case 1: @@ -3998,7 +4360,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WaitSSOLoginResponse); i { + switch v := v.(*WaitSSOLoginRequest); i { case 0: return &v.state case 1: @@ -4010,7 +4372,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpRequest); i { + switch v := v.(*WaitSSOLoginResponse); i { case 0: return &v.state case 1: @@ -4022,7 +4384,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpResponse); i { + switch v := v.(*UpRequest); i { case 0: return &v.state case 1: @@ -4034,7 +4396,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusRequest); i { + switch v := v.(*UpResponse); i { case 0: return &v.state case 1: @@ -4046,7 +4408,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusResponse); i { + switch v := v.(*StatusRequest); i { case 0: return &v.state case 1: @@ -4058,7 +4420,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DownRequest); i { + switch v := v.(*StatusResponse); i { case 0: return &v.state case 1: @@ -4070,7 +4432,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DownResponse); i { + switch v := v.(*DownRequest); i { case 0: return &v.state case 1: @@ -4082,7 +4444,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetConfigRequest); i { + switch v := v.(*DownResponse); i { case 0: return &v.state case 1: @@ -4094,7 +4456,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetConfigResponse); i { + switch v := v.(*GetConfigRequest); i { case 0: return &v.state case 1: @@ -4106,7 +4468,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeerState); i { + switch v := v.(*GetConfigResponse); i { case 0: return &v.state case 1: @@ -4118,7 +4480,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LocalPeerState); i { + switch v := v.(*PeerState); i { case 0: return &v.state case 1: @@ -4130,7 +4492,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignalState); i { + switch v := v.(*LocalPeerState); i { case 0: return &v.state case 1: @@ -4142,7 +4504,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ManagementState); i { + switch v := v.(*SignalState); i { case 0: return &v.state case 1: @@ -4154,7 +4516,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RelayState); i { + switch v := v.(*ManagementState); i { case 0: return &v.state case 1: @@ -4166,7 +4528,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NSGroupState); i { + switch v := v.(*RelayState); i { case 0: return &v.state case 1: @@ -4178,7 +4540,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FullStatus); i { + switch v := v.(*NSGroupState); i { case 0: return &v.state case 1: @@ -4190,7 +4552,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListNetworksRequest); i { + switch v := v.(*FullStatus); i { case 0: return &v.state case 1: @@ -4202,7 +4564,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListNetworksResponse); i { + switch v := v.(*ListNetworksRequest); i { case 0: return &v.state case 1: @@ -4214,7 +4576,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SelectNetworksRequest); i { + switch v := v.(*ListNetworksResponse); i { case 0: return &v.state case 1: @@ -4226,7 +4588,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SelectNetworksResponse); i { + switch v := v.(*SelectNetworksRequest); i { case 0: return &v.state case 1: @@ -4238,7 +4600,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IPList); i { + switch v := v.(*SelectNetworksResponse); i { case 0: return &v.state case 1: @@ -4250,7 +4612,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Network); i { + switch v := v.(*IPList); i { case 0: return &v.state case 1: @@ -4262,7 +4624,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DebugBundleRequest); i { + switch v := v.(*Network); i { case 0: return &v.state case 1: @@ -4274,7 +4636,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DebugBundleResponse); i { + switch v := v.(*PortInfo); i { case 0: return &v.state case 1: @@ -4286,7 +4648,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetLogLevelRequest); i { + switch v := v.(*ForwardingRule); i { case 0: return &v.state case 1: @@ -4298,7 +4660,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetLogLevelResponse); i { + switch v := v.(*ForwardingRulesResponse); i { case 0: return &v.state case 1: @@ -4310,7 +4672,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetLogLevelRequest); i { + switch v := v.(*DebugBundleRequest); i { case 0: return &v.state case 1: @@ -4322,7 +4684,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetLogLevelResponse); i { + switch v := v.(*DebugBundleResponse); i { case 0: return &v.state case 1: @@ -4334,7 +4696,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*State); i { + switch v := v.(*GetLogLevelRequest); i { case 0: return &v.state case 1: @@ -4346,7 +4708,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListStatesRequest); i { + switch v := v.(*GetLogLevelResponse); i { case 0: return &v.state case 1: @@ -4358,7 +4720,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListStatesResponse); i { + switch v := v.(*SetLogLevelRequest); i { case 0: return &v.state case 1: @@ -4370,7 +4732,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CleanStateRequest); i { + switch v := v.(*SetLogLevelResponse); i { case 0: return &v.state case 1: @@ -4382,7 +4744,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CleanStateResponse); i { + switch v := v.(*State); i { case 0: return &v.state case 1: @@ -4394,7 +4756,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteStateRequest); i { + switch v := v.(*ListStatesRequest); i { case 0: return &v.state case 1: @@ -4406,7 +4768,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteStateResponse); i { + switch v := v.(*ListStatesResponse); i { case 0: return &v.state case 1: @@ -4418,7 +4780,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetNetworkMapPersistenceRequest); i { + switch v := v.(*CleanStateRequest); i { case 0: return &v.state case 1: @@ -4430,7 +4792,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetNetworkMapPersistenceResponse); i { + switch v := v.(*CleanStateResponse); i { case 0: return &v.state case 1: @@ -4442,7 +4804,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TCPFlags); i { + switch v := v.(*DeleteStateRequest); i { case 0: return &v.state case 1: @@ -4454,7 +4816,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TracePacketRequest); i { + switch v := v.(*DeleteStateResponse); i { case 0: return &v.state case 1: @@ -4466,7 +4828,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TraceStage); i { + switch v := v.(*SetNetworkMapPersistenceRequest); i { case 0: return &v.state case 1: @@ -4478,7 +4840,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TracePacketResponse); i { + switch v := v.(*SetNetworkMapPersistenceResponse); i { case 0: return &v.state case 1: @@ -4490,7 +4852,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeRequest); i { + switch v := v.(*TCPFlags); i { case 0: return &v.state case 1: @@ -4502,7 +4864,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SystemEvent); i { + switch v := v.(*TracePacketRequest); i { case 0: return &v.state case 1: @@ -4514,7 +4876,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetEventsRequest); i { + switch v := v.(*TraceStage); i { case 0: return &v.state case 1: @@ -4526,6 +4888,54 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TracePacketResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SystemEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEventsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetEventsResponse); i { case 0: return &v.state @@ -4537,17 +4947,33 @@ func file_daemon_proto_init() { return nil } } + file_daemon_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PortInfo_Range); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } - file_daemon_proto_msgTypes[0].OneofWrappers = []interface{}{} - file_daemon_proto_msgTypes[41].OneofWrappers = []interface{}{} - file_daemon_proto_msgTypes[42].OneofWrappers = []interface{}{} + file_daemon_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_daemon_proto_msgTypes[26].OneofWrappers = []interface{}{ + (*PortInfo_Port)(nil), + (*PortInfo_Range_)(nil), + } + file_daemon_proto_msgTypes[45].OneofWrappers = []interface{}{} + file_daemon_proto_msgTypes[46].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_daemon_proto_rawDesc, NumEnums: 3, - NumMessages: 50, + NumMessages: 55, NumExtensions: 0, NumServices: 1, }, diff --git a/client/proto/daemon.proto b/client/proto/daemon.proto index b1a6a6614..49e577853 100644 --- a/client/proto/daemon.proto +++ b/client/proto/daemon.proto @@ -8,6 +8,8 @@ option go_package = "/proto"; package daemon; +message EmptyRequest {} + service DaemonService { // Login uses setup key to prepare configuration for the daemon. rpc Login(LoginRequest) returns (LoginResponse) {} @@ -37,6 +39,8 @@ service DaemonService { // Deselect specific routes rpc DeselectNetworks(SelectNetworksRequest) returns (SelectNetworksResponse) {} + rpc ForwardingRules(EmptyRequest) returns (ForwardingRulesResponse) {} + // DebugBundle creates a debug bundle rpc DebugBundle(DebugBundleRequest) returns (DebugBundleResponse) {} @@ -267,10 +271,12 @@ message FullStatus { repeated PeerState peers = 4; repeated RelayState relays = 5; repeated NSGroupState dns_servers = 6; + int32 NumberOfForwardingRules = 8; repeated SystemEvent events = 7; } +// Networks message ListNetworksRequest { } @@ -291,7 +297,6 @@ message IPList { repeated string ips = 1; } - message Network { string ID = 1; string range = 2; @@ -300,6 +305,33 @@ message Network { map resolvedIPs = 5; } +// ForwardingRules +message PortInfo { + oneof portSelection { + uint32 port = 1; + Range range = 2; + } + + message Range { + uint32 start = 1; + uint32 end = 2; + } +} + +message ForwardingRule { + string protocol = 1; + PortInfo destinationPort = 2; + string translatedAddress = 3; + string translatedHostname = 4; + PortInfo translatedPort = 5; +} + +message ForwardingRulesResponse { + repeated ForwardingRule rules = 1; +} + + +// DebugBundler message DebugBundleRequest { bool anonymize = 1; string status = 2; diff --git a/client/proto/daemon_grpc.pb.go b/client/proto/daemon_grpc.pb.go index 0cb2a7c59..6251f7c52 100644 --- a/client/proto/daemon_grpc.pb.go +++ b/client/proto/daemon_grpc.pb.go @@ -37,6 +37,7 @@ type DaemonServiceClient interface { SelectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) // Deselect specific routes DeselectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) + ForwardingRules(ctx context.Context, in *EmptyRequest, opts ...grpc.CallOption) (*ForwardingRulesResponse, error) // DebugBundle creates a debug bundle DebugBundle(ctx context.Context, in *DebugBundleRequest, opts ...grpc.CallOption) (*DebugBundleResponse, error) // GetLogLevel gets the log level of the daemon @@ -145,6 +146,15 @@ func (c *daemonServiceClient) DeselectNetworks(ctx context.Context, in *SelectNe return out, nil } +func (c *daemonServiceClient) ForwardingRules(ctx context.Context, in *EmptyRequest, opts ...grpc.CallOption) (*ForwardingRulesResponse, error) { + out := new(ForwardingRulesResponse) + err := c.cc.Invoke(ctx, "/daemon.DaemonService/ForwardingRules", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *daemonServiceClient) DebugBundle(ctx context.Context, in *DebugBundleRequest, opts ...grpc.CallOption) (*DebugBundleResponse, error) { out := new(DebugBundleResponse) err := c.cc.Invoke(ctx, "/daemon.DaemonService/DebugBundle", in, out, opts...) @@ -281,6 +291,7 @@ type DaemonServiceServer interface { SelectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) // Deselect specific routes DeselectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) + ForwardingRules(context.Context, *EmptyRequest) (*ForwardingRulesResponse, error) // DebugBundle creates a debug bundle DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error) // GetLogLevel gets the log level of the daemon @@ -332,6 +343,9 @@ func (UnimplementedDaemonServiceServer) SelectNetworks(context.Context, *SelectN func (UnimplementedDaemonServiceServer) DeselectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeselectNetworks not implemented") } +func (UnimplementedDaemonServiceServer) ForwardingRules(context.Context, *EmptyRequest) (*ForwardingRulesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ForwardingRules not implemented") +} func (UnimplementedDaemonServiceServer) DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DebugBundle not implemented") } @@ -537,6 +551,24 @@ func _DaemonService_DeselectNetworks_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _DaemonService_ForwardingRules_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EmptyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DaemonServiceServer).ForwardingRules(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/daemon.DaemonService/ForwardingRules", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DaemonServiceServer).ForwardingRules(ctx, req.(*EmptyRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _DaemonService_DebugBundle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DebugBundleRequest) if err := dec(in); err != nil { @@ -763,6 +795,10 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeselectNetworks", Handler: _DaemonService_DeselectNetworks_Handler, }, + { + MethodName: "ForwardingRules", + Handler: _DaemonService_ForwardingRules_Handler, + }, { MethodName: "DebugBundle", Handler: _DaemonService_DebugBundle_Handler, diff --git a/client/server/forwardingrules.go b/client/server/forwardingrules.go new file mode 100644 index 000000000..3d706c36d --- /dev/null +++ b/client/server/forwardingrules.go @@ -0,0 +1,54 @@ +package server + +import ( + "context" + + firewall "github.com/netbirdio/netbird/client/firewall/manager" + "github.com/netbirdio/netbird/client/proto" +) + +func (s *Server) ForwardingRules(context.Context, *proto.EmptyRequest) (*proto.ForwardingRulesResponse, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + rules := s.statusRecorder.ForwardingRules() + responseRules := make([]*proto.ForwardingRule, 0, len(rules)) + for _, rule := range rules { + respRule := &proto.ForwardingRule{ + Protocol: string(rule.Protocol), + DestinationPort: portToProto(rule.DestinationPort), + TranslatedAddress: rule.TranslatedAddress.String(), + TranslatedHostname: s.hostNameByTranslateAddress(rule.TranslatedAddress.String()), + TranslatedPort: portToProto(rule.TranslatedPort), + } + responseRules = append(responseRules, respRule) + + } + + return &proto.ForwardingRulesResponse{Rules: responseRules}, nil +} + +func (s *Server) hostNameByTranslateAddress(ip string) string { + hostName, ok := s.statusRecorder.PeerByIP(ip) + if !ok { + return ip + } + + return hostName +} + +func portToProto(port firewall.Port) *proto.PortInfo { + var portInfo proto.PortInfo + + if !port.IsRange { + portInfo.PortSelection = &proto.PortInfo_Port{Port: uint32(port.Values[0])} + } else { + portInfo.PortSelection = &proto.PortInfo_Range_{ + Range: &proto.PortInfo_Range{ + Start: uint32(port.Values[0]), + End: uint32(port.Values[1]), + }, + } + } + return &portInfo +} diff --git a/client/server/server.go b/client/server/server.go index 348fb9872..8907f541f 100644 --- a/client/server/server.go +++ b/client/server/server.go @@ -810,6 +810,7 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus { pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled pbFullStatus.LocalPeerState.Networks = maps.Keys(fullStatus.LocalPeerState.Routes) + pbFullStatus.NumberOfForwardingRules = int32(fullStatus.NumOfForwardingRules) for _, peerState := range fullStatus.Peers { pbPeerState := &proto.PeerState{ diff --git a/client/server/server_test.go b/client/server/server_test.go index d6b651a79..0c0f32fec 100644 --- a/client/server/server_test.go +++ b/client/server/server_test.go @@ -20,6 +20,7 @@ import ( mgmtProto "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/settings" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" @@ -128,7 +129,7 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) require.NoError(t, err) - accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics) + accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock()) if err != nil { return nil, "", err } diff --git a/client/status/status.go b/client/status/status.go index 2d11ee3ba..43acc9197 100644 --- a/client/status/status.go +++ b/client/status/status.go @@ -80,21 +80,22 @@ type NsServerGroupStateOutput struct { } type OutputOverview struct { - Peers PeersStateOutput `json:"peers" yaml:"peers"` - CliVersion string `json:"cliVersion" yaml:"cliVersion"` - DaemonVersion string `json:"daemonVersion" yaml:"daemonVersion"` - ManagementState ManagementStateOutput `json:"management" yaml:"management"` - SignalState SignalStateOutput `json:"signal" yaml:"signal"` - Relays RelayStateOutput `json:"relays" yaml:"relays"` - IP string `json:"netbirdIp" yaml:"netbirdIp"` - PubKey string `json:"publicKey" yaml:"publicKey"` - KernelInterface bool `json:"usesKernelInterface" yaml:"usesKernelInterface"` - FQDN string `json:"fqdn" yaml:"fqdn"` - RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"` - RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"` - Networks []string `json:"networks" yaml:"networks"` - NSServerGroups []NsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"` - Events []SystemEventOutput `json:"events" yaml:"events"` + Peers PeersStateOutput `json:"peers" yaml:"peers"` + CliVersion string `json:"cliVersion" yaml:"cliVersion"` + DaemonVersion string `json:"daemonVersion" yaml:"daemonVersion"` + ManagementState ManagementStateOutput `json:"management" yaml:"management"` + SignalState SignalStateOutput `json:"signal" yaml:"signal"` + Relays RelayStateOutput `json:"relays" yaml:"relays"` + IP string `json:"netbirdIp" yaml:"netbirdIp"` + PubKey string `json:"publicKey" yaml:"publicKey"` + KernelInterface bool `json:"usesKernelInterface" yaml:"usesKernelInterface"` + FQDN string `json:"fqdn" yaml:"fqdn"` + RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"` + RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"` + Networks []string `json:"networks" yaml:"networks"` + NumberOfForwardingRules int `json:"forwardingRules" yaml:"forwardingRules"` + NSServerGroups []NsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"` + Events []SystemEventOutput `json:"events" yaml:"events"` } func ConvertToStatusOutputOverview(resp *proto.StatusResponse, anon bool, statusFilter string, prefixNamesFilter []string, prefixNamesFilterMap map[string]struct{}, ipsFilter map[string]struct{}) OutputOverview { @@ -118,21 +119,22 @@ func ConvertToStatusOutputOverview(resp *proto.StatusResponse, anon bool, status peersOverview := mapPeers(resp.GetFullStatus().GetPeers(), statusFilter, prefixNamesFilter, prefixNamesFilterMap, ipsFilter) overview := OutputOverview{ - Peers: peersOverview, - CliVersion: version.NetbirdVersion(), - DaemonVersion: resp.GetDaemonVersion(), - ManagementState: managementOverview, - SignalState: signalOverview, - Relays: relayOverview, - IP: pbFullStatus.GetLocalPeerState().GetIP(), - PubKey: pbFullStatus.GetLocalPeerState().GetPubKey(), - KernelInterface: pbFullStatus.GetLocalPeerState().GetKernelInterface(), - FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(), - RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(), - RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(), - Networks: pbFullStatus.GetLocalPeerState().GetNetworks(), - NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()), - Events: mapEvents(pbFullStatus.GetEvents()), + Peers: peersOverview, + CliVersion: version.NetbirdVersion(), + DaemonVersion: resp.GetDaemonVersion(), + ManagementState: managementOverview, + SignalState: signalOverview, + Relays: relayOverview, + IP: pbFullStatus.GetLocalPeerState().GetIP(), + PubKey: pbFullStatus.GetLocalPeerState().GetPubKey(), + KernelInterface: pbFullStatus.GetLocalPeerState().GetKernelInterface(), + FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(), + RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(), + RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(), + Networks: pbFullStatus.GetLocalPeerState().GetNetworks(), + NumberOfForwardingRules: int(pbFullStatus.GetNumberOfForwardingRules()), + NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()), + Events: mapEvents(pbFullStatus.GetEvents()), } if anon { @@ -403,6 +405,7 @@ func ParseGeneralSummary(overview OutputOverview, showURL bool, showRelays bool, "Interface type: %s\n"+ "Quantum resistance: %s\n"+ "Networks: %s\n"+ + "Forwarding rules: %d\n"+ "Peers count: %s\n", fmt.Sprintf("%s/%s%s", goos, goarch, goarm), overview.DaemonVersion, @@ -416,6 +419,7 @@ func ParseGeneralSummary(overview OutputOverview, showURL bool, showRelays bool, interfaceTypeString, rosenpassEnabledStatus, networks, + overview.NumberOfForwardingRules, peersCountString, ) return summary diff --git a/client/status/status_test.go b/client/status/status_test.go index 24c4827d3..e48b441f5 100644 --- a/client/status/status_test.go +++ b/client/status/status_test.go @@ -360,6 +360,7 @@ func TestParsingToJSON(t *testing.T) { "networks": [ "10.10.0.0/24" ], + "forwardingRules": 0, "dnsServers": [ { "servers": [ @@ -467,6 +468,7 @@ quantumResistance: false quantumResistancePermissive: false networks: - 10.10.0.0/24 +forwardingRules: 0 dnsServers: - servers: - 8.8.8.8:53 @@ -547,6 +549,7 @@ NetBird IP: 192.168.178.100/16 Interface type: Kernel Quantum resistance: false Networks: 10.10.0.0/24 +Forwarding rules: 0 Peers count: 2/2 Connected `, lastConnectionUpdate1, lastHandshake1, lastConnectionUpdate2, lastHandshake2, runtime.GOOS, runtime.GOARCH, overview.CliVersion) @@ -568,6 +571,7 @@ NetBird IP: 192.168.178.100/16 Interface type: Kernel Quantum resistance: false Networks: 10.10.0.0/24 +Forwarding rules: 0 Peers count: 2/2 Connected ` diff --git a/go.mod b/go.mod index 76605e155..e48d8ee0d 100644 --- a/go.mod +++ b/go.mod @@ -19,13 +19,13 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/vishvananda/netlink v1.2.1-beta.2 - golang.org/x/crypto v0.32.0 - golang.org/x/sys v0.29.0 + golang.org/x/crypto v0.36.0 + golang.org/x/sys v0.31.0 golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 golang.zx2c4.com/wireguard/windows v0.5.3 - google.golang.org/grpc v1.70.0 - google.golang.org/protobuf v1.36.4 + google.golang.org/grpc v1.64.1 + google.golang.org/protobuf v1.34.2 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) @@ -60,7 +60,7 @@ require ( github.com/miekg/dns v1.1.59 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/nadoo/ipset v0.5.0 - github.com/netbirdio/management-integrations/integrations v0.0.0-20250220173202-e599d83524fc + github.com/netbirdio/management-integrations/integrations v0.0.0-20250307154727-58660ea9a141 github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d github.com/okta/okta-sdk-golang/v2 v2.18.0 github.com/oschwald/maxminddb-golang v1.12.0 @@ -77,27 +77,27 @@ require ( github.com/shirou/gopsutil/v3 v3.24.4 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.31.0 github.com/testcontainers/testcontainers-go/modules/mysql v0.31.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.31.0 github.com/things-go/go-socks5 v0.0.4 github.com/yusufpapurcu/wmi v1.2.4 github.com/zcalusic/sysinfo v1.1.3 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 - go.opentelemetry.io/otel v1.34.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 + go.opentelemetry.io/otel v1.26.0 go.opentelemetry.io/otel/exporters/prometheus v0.48.0 - go.opentelemetry.io/otel/metric v1.34.0 - go.opentelemetry.io/otel/sdk/metric v1.32.0 + go.opentelemetry.io/otel/metric v1.26.0 + go.opentelemetry.io/otel/sdk/metric v1.26.0 go.uber.org/zap v1.27.0 goauthentik.io/api/v3 v3.2023051.3 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a - golang.org/x/net v0.34.0 - golang.org/x/oauth2 v0.26.0 - golang.org/x/sync v0.10.0 - golang.org/x/term v0.28.0 - google.golang.org/api v0.220.0 + golang.org/x/net v0.33.0 + golang.org/x/oauth2 v0.19.0 + golang.org/x/sync v0.12.0 + golang.org/x/term v0.30.0 + google.golang.org/api v0.177.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.5.7 gorm.io/driver/postgres v1.5.7 @@ -107,9 +107,9 @@ require ( ) require ( - cloud.google.com/go/auth v0.14.1 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect - cloud.google.com/go/compute/metadata v0.6.0 // indirect + cloud.google.com/go/auth v0.3.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -152,7 +152,7 @@ require ( github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect @@ -161,11 +161,12 @@ require ( github.com/go-text/render v0.2.0 // indirect github.com/go-text/typesetting v0.2.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.2 // indirect github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect - github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect - github.com/googleapis/gax-go/v2 v2.14.1 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -221,19 +222,20 @@ require ( github.com/vishvananda/netns v0.0.4 // indirect github.com/yuin/goldmark v1.7.1 // indirect github.com/zeebo/blake3 v0.2.3 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel/sdk v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect + go.opentelemetry.io/otel/sdk v1.26.0 // indirect + go.opentelemetry.io/otel/trace v1.26.0 // indirect go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/image v0.18.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.10.0 // indirect + golang.org/x/text v0.23.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240509183442-62759503f434 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect diff --git a/go.sum b/go.sum index 069b378d6..2794c7546 100644 --- a/go.sum +++ b/go.sum @@ -18,10 +18,10 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0= -cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go/auth v0.3.0 h1:PRyzEpGfx/Z9e8+lHsbkoUVXD0gnu4MNmm7Gp8TQNIs= +cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -29,8 +29,8 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= -cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -225,8 +225,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -263,12 +263,14 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.3 h1:oDTdz9f5VGVVNGu/Q7UXKWYsD0873HXLHdJUNBsSEKM= -github.com/golang/glog v1.2.3/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -343,18 +345,18 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= -github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= -github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gopacket/gopacket v1.1.1 h1:zbx9F9d6A7sWNkFKrvMBZTfGgxFoY4NgUudFVVHMfcw= github.com/gopacket/gopacket v1.1.1/go.mod h1:HavMeONEl7W9036of9LbSWoonqhH7HA1+ZRO+rMIvFs= @@ -527,8 +529,8 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250220173202-e599d83524fc h1:18xvjOy2tZVIK7rihNpf9DF/3mAiljYKWaQlWa9vJgI= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250220173202-e599d83524fc/go.mod h1:izUUs1NT7ja+PwSX3kJ7ox8Kkn478tboBJSjL4kU6J0= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250307154727-58660ea9a141 h1:GZUkZd9ZMBGahNt+AbYYvZrSMpOnaBLjHiBbloOE7sc= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250307154727-58660ea9a141/go.mod h1:A5QUfEZb5J3tw8EUB9e3q7Bgd/JtC0WlFT1onf3HPCY= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d h1:bRq5TKgC7Iq20pDiuC54yXaWnAVeS5PdGpSokFTlR28= @@ -617,8 +619,8 @@ github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KW github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.8.0 h1:P2KMzcFwrPoSjkF1WLRPsp3UMLyql8L4v9hQpVeK5so= github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= @@ -683,11 +685,11 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/testcontainers/testcontainers-go v0.31.0 h1:W0VwIhcEVhRflwL9as3dhY6jXjVCA27AkmbnZ+UTh3U= github.com/testcontainers/testcontainers-go v0.31.0/go.mod h1:D2lAoA0zUFiSY+eAflqK5mcUx/A5hrrORaEQrd0SefI= @@ -739,28 +741,28 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= +go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/exporters/prometheus v0.48.0 h1:sBQe3VNGUjY9IKWQC6z2lNqa5iGbDSxhs60ABwK4y0s= go.opentelemetry.io/otel/exporters/prometheus v0.48.0/go.mod h1:DtrbMzoZWwQHyrQmCfLam5DZbnmorsGbOtTbYHycU5o= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= +go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8= +go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= +go.opentelemetry.io/otel/sdk/metric v1.26.0 h1:cWSks5tfriHPdWFnl+qpX3P681aAYqlZHcAyHw5aU9Y= +go.opentelemetry.io/otel/sdk/metric v1.26.0/go.mod h1:ClMFFknnThJCksebJwz7KIyEDHO+nTB6gK8obLy8RyE= +go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -788,8 +790,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -885,8 +887,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -900,8 +902,8 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= -golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -915,8 +917,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -989,8 +991,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -998,8 +1000,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1014,13 +1016,13 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= -golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1114,8 +1116,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.220.0 h1:3oMI4gdBgB72WFVwE1nerDD8W3HUOS4kypK6rRLbGns= -google.golang.org/api v0.220.0/go.mod h1:26ZAlY6aN/8WgpCzjPNy18QpYaz7Zgg1h0qe1GkZEmY= +google.golang.org/api v0.177.0 h1:8a0p/BbPa65GlqGWtUKxot4p0TV8OGOfyTjtmkXNXmk= +google.golang.org/api v0.177.0/go.mod h1:srbhue4MLjkjbkux5p3dw/ocYOSZTaIEvf7bCOnFQDw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1164,11 +1166,10 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= +google.golang.org/genproto/googleapis/api v0.0.0-20240509183442-62759503f434 h1:OpXbo8JnN8+jZGPrL4SSfaDjSCjupr8lXyBAbexEm/U= +google.golang.org/genproto/googleapis/api v0.0.0-20240509183442-62759503f434/go.mod h1:FfiGhwUm6CJviekPrc0oJ+7h29e+DmWU6UtjX0ZvI7Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1189,8 +1190,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= -google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1205,8 +1206,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/management/client/client_test.go b/management/client/client_test.go index 21f6b79ad..73427b38a 100644 --- a/management/client/client_test.go +++ b/management/client/client_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/settings" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" @@ -72,7 +73,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) { metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) require.NoError(t, err) - accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics) + accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock()) if err != nil { t.Fatal(err) } diff --git a/management/cmd/management.go b/management/cmd/management.go index e087a3d67..b8f5f4233 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -35,6 +35,8 @@ import ( "github.com/netbirdio/management-integrations/integrations" + "github.com/netbirdio/netbird/management/server/peers" + "github.com/netbirdio/netbird/encryption" "github.com/netbirdio/netbird/formatter/hook" mgmtProto "github.com/netbirdio/netbird/management/proto" @@ -199,8 +201,15 @@ var ( if err != nil { return fmt.Errorf("failed to initialize integrated peer validator: %v", err) } + + userManager := users.NewManager(store) + settingsManager := settings.NewManager(store) + permissionsManager := permissions.NewManager(userManager, settingsManager) + peersManager := peers.NewManager(store, permissionsManager) + proxyController := integrations.NewController(store) + accountManager, err := server.BuildManager(ctx, store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain, - dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerValidator, appMetrics) + dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerValidator, appMetrics, proxyController) if err != nil { return fmt.Errorf("failed to build default manager: %v", err) } @@ -261,15 +270,14 @@ var ( config.HttpConfig.AuthUserIDClaim, config.GetAuthAudiences(), config.HttpConfig.IdpSignKeyRefreshEnabled) - userManager := users.NewManager(store) - settingsManager := settings.NewManager(store) - permissionsManager := permissions.NewManager(userManager, settingsManager) + groupsManager := groups.NewManager(store, permissionsManager, accountManager) resourcesManager := resources.NewManager(store, permissionsManager, groupsManager, accountManager) routersManager := routers.NewManager(store, permissionsManager, accountManager) networksManager := networks.NewManager(store, permissionsManager, resourcesManager, routersManager, accountManager) - httpAPIHandler, err := nbhttp.NewAPIHandler(ctx, accountManager, networksManager, resourcesManager, routersManager, groupsManager, geo, authManager, appMetrics, config, integratedPeerValidator) + httpAPIHandler, err := nbhttp.NewAPIHandler(ctx, accountManager, networksManager, resourcesManager, routersManager, groupsManager, geo, authManager, appMetrics, integratedPeerValidator, proxyController, permissionsManager, peersManager) + if err != nil { return fmt.Errorf("failed creating HTTP API handler: %v", err) } diff --git a/management/domain/list.go b/management/domain/list.go index 413a23442..b6090c717 100644 --- a/management/domain/list.go +++ b/management/domain/list.go @@ -1,6 +1,9 @@ package domain -import "strings" +import ( + "sort" + "strings" +) type List []Domain @@ -60,6 +63,27 @@ func (d List) PunycodeString() string { return strings.Join(d.ToPunycodeList(), ", ") } +func (d List) Equal(domains List) bool { + if len(d) != len(domains) { + return false + } + + sort.Slice(d, func(i, j int) bool { + return d[i] < d[j] + }) + + sort.Slice(domains, func(i, j int) bool { + return domains[i] < domains[j] + }) + + for i, domain := range d { + if domain != domains[i] { + return false + } + } + return true +} + // FromStringList creates a DomainList from a slice of string. func FromStringList(s []string) (List, error) { var dl List diff --git a/management/domain/list_test.go b/management/domain/list_test.go new file mode 100644 index 000000000..5000af01c --- /dev/null +++ b/management/domain/list_test.go @@ -0,0 +1,49 @@ +package domain + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_EqualReturnsTrueForIdenticalLists(t *testing.T) { + list1 := List{"domain1", "domain2", "domain3"} + list2 := List{"domain1", "domain2", "domain3"} + + assert.True(t, list1.Equal(list2)) +} + +func Test_EqualReturnsFalseForDifferentLengths(t *testing.T) { + list1 := List{"domain1", "domain2"} + list2 := List{"domain1", "domain2", "domain3"} + + assert.False(t, list1.Equal(list2)) +} + +func Test_EqualReturnsFalseForDifferentElements(t *testing.T) { + list1 := List{"domain1", "domain2", "domain3"} + list2 := List{"domain1", "domain4", "domain3"} + + assert.False(t, list1.Equal(list2)) +} + +func Test_EqualReturnsTrueForUnsortedIdenticalLists(t *testing.T) { + list1 := List{"domain3", "domain1", "domain2"} + list2 := List{"domain1", "domain2", "domain3"} + + assert.True(t, list1.Equal(list2)) +} + +func Test_EqualReturnsFalseForEmptyAndNonEmptyList(t *testing.T) { + list1 := List{} + list2 := List{"domain1"} + + assert.False(t, list1.Equal(list2)) +} + +func Test_EqualReturnsTrueForBothEmptyLists(t *testing.T) { + list1 := List{} + list2 := List{} + + assert.True(t, list1.Equal(list2)) +} diff --git a/management/proto/management.pb.go b/management/proto/management.pb.go index 2cd00783e..cd69d0565 100644 --- a/management/proto/management.pb.go +++ b/management/proto/management.pb.go @@ -1607,7 +1607,8 @@ type NetworkMap struct { // RoutesFirewallRules represents a list of routes firewall rules to be applied to peer RoutesFirewallRules []*RouteFirewallRule `protobuf:"bytes,10,rep,name=routesFirewallRules,proto3" json:"routesFirewallRules,omitempty"` // RoutesFirewallRulesIsEmpty indicates whether RouteFirewallRule array is empty or not to bypass protobuf null and empty array equality. - RoutesFirewallRulesIsEmpty bool `protobuf:"varint,11,opt,name=routesFirewallRulesIsEmpty,proto3" json:"routesFirewallRulesIsEmpty,omitempty"` + RoutesFirewallRulesIsEmpty bool `protobuf:"varint,11,opt,name=routesFirewallRulesIsEmpty,proto3" json:"routesFirewallRulesIsEmpty,omitempty"` + ForwardingRules []*ForwardingRule `protobuf:"bytes,12,rep,name=forwardingRules,proto3" json:"forwardingRules,omitempty"` } func (x *NetworkMap) Reset() { @@ -1719,6 +1720,13 @@ func (x *NetworkMap) GetRoutesFirewallRulesIsEmpty() bool { return false } +func (x *NetworkMap) GetForwardingRules() []*ForwardingRule { + if x != nil { + return x.ForwardingRules + } + return nil +} + // RemotePeerConfig represents a configuration of a remote peer. // The properties are used to configure WireGuard Peers sections type RemotePeerConfig struct { @@ -3004,6 +3012,82 @@ func (x *RouteFirewallRule) GetCustomProtocol() uint32 { return 0 } +type ForwardingRule struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Protocol of the forwarding rule + Protocol RuleProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=management.RuleProtocol" json:"protocol,omitempty"` + // portInfo is the ingress destination port information, where the traffic arrives in the gateway node + DestinationPort *PortInfo `protobuf:"bytes,2,opt,name=destinationPort,proto3" json:"destinationPort,omitempty"` + // IP address of the translated address (remote peer) to send traffic to + // todo type pending + TranslatedAddress []byte `protobuf:"bytes,3,opt,name=translatedAddress,proto3" json:"translatedAddress,omitempty"` + // Translated port information, where the traffic should be forwarded to + TranslatedPort *PortInfo `protobuf:"bytes,4,opt,name=translatedPort,proto3" json:"translatedPort,omitempty"` +} + +func (x *ForwardingRule) Reset() { + *x = ForwardingRule{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForwardingRule) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForwardingRule) ProtoMessage() {} + +func (x *ForwardingRule) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ForwardingRule.ProtoReflect.Descriptor instead. +func (*ForwardingRule) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{37} +} + +func (x *ForwardingRule) GetProtocol() RuleProtocol { + if x != nil { + return x.Protocol + } + return RuleProtocol_UNKNOWN +} + +func (x *ForwardingRule) GetDestinationPort() *PortInfo { + if x != nil { + return x.DestinationPort + } + return nil +} + +func (x *ForwardingRule) GetTranslatedAddress() []byte { + if x != nil { + return x.TranslatedAddress + } + return nil +} + +func (x *ForwardingRule) GetTranslatedPort() *PortInfo { + if x != nil { + return x.TranslatedPort + } + return nil +} + type PortInfo_Range struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3016,7 +3100,7 @@ type PortInfo_Range struct { func (x *PortInfo_Range) Reset() { *x = PortInfo_Range{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[37] + mi := &file_management_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3029,7 +3113,7 @@ func (x *PortInfo_Range) String() string { func (*PortInfo_Range) ProtoMessage() {} func (x *PortInfo_Range) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[37] + mi := &file_management_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3258,7 +3342,7 @@ var file_management_proto_rawDesc = []byte{ 0x6e, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x65, 0x72, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xf3, 0x04, 0x0a, 0x0a, 0x4e, + 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xb9, 0x05, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, @@ -3298,218 +3382,238 @@ var file_management_proto_rawDesc = []byte{ 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, - 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, - 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, - 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, - 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, - 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, + 0x12, 0x44, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, + 0x6c, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, + 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, + 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, + 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, + 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, + 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, - 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, - 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, - 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x1e, 0x0a, 0x1c, 0x50, 0x4b, 0x43, - 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, - 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x15, 0x50, 0x4b, 0x43, - 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, - 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xea, 0x02, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, + 0x1e, 0x0a, 0x1c, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x5b, 0x0a, 0x15, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xea, 0x02, 0x0a, + 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, + 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, + 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, + 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, - 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x73, 0x65, - 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, - 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, - 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x18, - 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, - 0x52, 0x4c, 0x73, 0x22, 0xed, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, - 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, - 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a, - 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, - 0x61, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, - 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x44, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, - 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x10, - 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, - 0x12, 0x38, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x0b, 0x43, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x0a, 0x43, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x12, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, - 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x52, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x0c, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x43, 0x6c, 0x61, - 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x4e, - 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38, - 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x4e, 0x61, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x14, - 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x53, 0x65, 0x61, 0x72, - 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e, - 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, - 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, - 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x8b, 0x02, 0x0a, 0x0c, 0x46, - 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x50, - 0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x65, 0x65, - 0x72, 0x49, 0x50, 0x12, 0x37, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, - 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x38, 0x0a, 0x0e, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x65, - 0x74, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, - 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, - 0x61, 0x63, 0x22, 0x1e, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x14, 0x0a, 0x05, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x46, 0x69, 0x6c, - 0x65, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x14, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, - 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, - 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0x2f, 0x0a, 0x05, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x70, 0x6f, - 0x72, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd1, 0x02, 0x0a, 0x11, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, - 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x30, 0x0a, - 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x1c, 0x0a, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x12, 0x18, 0x0a, - 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2a, - 0x4c, 0x0a, 0x0c, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, - 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, - 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, - 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50, 0x10, - 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x05, 0x2a, 0x20, 0x0a, - 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06, - 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x2a, - 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, - 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52, 0x4f, - 0x50, 0x10, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, - 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, - 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, - 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, + 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, + 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, + 0x1e, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, + 0x34, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x55, 0x52, 0x4c, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x22, 0xed, 0x01, 0x0a, 0x05, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, + 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, + 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, + 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, + 0x65, 0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, + 0x44, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, + 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x09, 0x44, 0x4e, + 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x47, 0x0a, + 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, + 0x6f, 0x6e, 0x65, 0x52, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, + 0x22, 0x58, 0x0a, 0x0a, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x52, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x0c, 0x53, 0x69, + 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, + 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, + 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, + 0x22, 0x8b, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x12, 0x37, 0x0a, 0x09, 0x44, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x08, + 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x38, + 0x0a, 0x0e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x22, 0x1e, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x72, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x1a, + 0x2f, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, + 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, + 0x42, 0x0f, 0x0a, 0x0d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0xd1, 0x02, 0x0a, 0x11, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, + 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, + 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, + 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x6f, 0x72, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, + 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, + 0x6d, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x26, 0x0a, + 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0xf2, 0x01, 0x0a, 0x0e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3e, + 0x0a, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x72, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2c, + 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x6c, 0x61, 0x74, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3c, 0x0a, 0x0e, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x2a, 0x4c, 0x0a, 0x0c, 0x52, 0x75, + 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, + 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, + 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, + 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x05, 0x2a, 0x20, 0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, + 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, + 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, + 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, + 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x32, 0x90, + 0x04, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x4d, - 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, + 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, + 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, + 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -3525,7 +3629,7 @@ func file_management_proto_rawDescGZIP() []byte { } var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 38) +var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 39) var file_management_proto_goTypes = []interface{}{ (RuleProtocol)(0), // 0: management.RuleProtocol (RuleDirection)(0), // 1: management.RuleDirection @@ -3569,8 +3673,9 @@ var file_management_proto_goTypes = []interface{}{ (*Checks)(nil), // 39: management.Checks (*PortInfo)(nil), // 40: management.PortInfo (*RouteFirewallRule)(nil), // 41: management.RouteFirewallRule - (*PortInfo_Range)(nil), // 42: management.PortInfo.Range - (*timestamppb.Timestamp)(nil), // 43: google.protobuf.Timestamp + (*ForwardingRule)(nil), // 42: management.ForwardingRule + (*PortInfo_Range)(nil), // 43: management.PortInfo.Range + (*timestamppb.Timestamp)(nil), // 44: google.protobuf.Timestamp } var file_management_proto_depIdxs = []int32{ 14, // 0: management.SyncRequest.meta:type_name -> management.PeerSystemMeta @@ -3589,7 +3694,7 @@ var file_management_proto_depIdxs = []int32{ 18, // 13: management.LoginResponse.netbirdConfig:type_name -> management.NetbirdConfig 22, // 14: management.LoginResponse.peerConfig:type_name -> management.PeerConfig 39, // 15: management.LoginResponse.Checks:type_name -> management.Checks - 43, // 16: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp + 44, // 16: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp 19, // 17: management.NetbirdConfig.stuns:type_name -> management.HostConfig 21, // 18: management.NetbirdConfig.turns:type_name -> management.ProtectedHostConfig 19, // 19: management.NetbirdConfig.signal:type_name -> management.HostConfig @@ -3604,41 +3709,45 @@ var file_management_proto_depIdxs = []int32{ 24, // 28: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig 37, // 29: management.NetworkMap.FirewallRules:type_name -> management.FirewallRule 41, // 30: management.NetworkMap.routesFirewallRules:type_name -> management.RouteFirewallRule - 25, // 31: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig - 4, // 32: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider - 30, // 33: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig - 30, // 34: management.PKCEAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig - 35, // 35: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup - 33, // 36: management.DNSConfig.CustomZones:type_name -> management.CustomZone - 34, // 37: management.CustomZone.Records:type_name -> management.SimpleRecord - 36, // 38: management.NameServerGroup.NameServers:type_name -> management.NameServer - 1, // 39: management.FirewallRule.Direction:type_name -> management.RuleDirection - 2, // 40: management.FirewallRule.Action:type_name -> management.RuleAction - 0, // 41: management.FirewallRule.Protocol:type_name -> management.RuleProtocol - 40, // 42: management.FirewallRule.PortInfo:type_name -> management.PortInfo - 42, // 43: management.PortInfo.range:type_name -> management.PortInfo.Range - 2, // 44: management.RouteFirewallRule.action:type_name -> management.RuleAction - 0, // 45: management.RouteFirewallRule.protocol:type_name -> management.RuleProtocol - 40, // 46: management.RouteFirewallRule.portInfo:type_name -> management.PortInfo - 5, // 47: management.ManagementService.Login:input_type -> management.EncryptedMessage - 5, // 48: management.ManagementService.Sync:input_type -> management.EncryptedMessage - 17, // 49: management.ManagementService.GetServerKey:input_type -> management.Empty - 17, // 50: management.ManagementService.isHealthy:input_type -> management.Empty - 5, // 51: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage - 5, // 52: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage - 5, // 53: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage - 5, // 54: management.ManagementService.Login:output_type -> management.EncryptedMessage - 5, // 55: management.ManagementService.Sync:output_type -> management.EncryptedMessage - 16, // 56: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse - 17, // 57: management.ManagementService.isHealthy:output_type -> management.Empty - 5, // 58: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage - 5, // 59: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage - 17, // 60: management.ManagementService.SyncMeta:output_type -> management.Empty - 54, // [54:61] is the sub-list for method output_type - 47, // [47:54] is the sub-list for method input_type - 47, // [47:47] is the sub-list for extension type_name - 47, // [47:47] is the sub-list for extension extendee - 0, // [0:47] is the sub-list for field type_name + 42, // 31: management.NetworkMap.forwardingRules:type_name -> management.ForwardingRule + 25, // 32: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig + 4, // 33: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider + 30, // 34: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig + 30, // 35: management.PKCEAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig + 35, // 36: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup + 33, // 37: management.DNSConfig.CustomZones:type_name -> management.CustomZone + 34, // 38: management.CustomZone.Records:type_name -> management.SimpleRecord + 36, // 39: management.NameServerGroup.NameServers:type_name -> management.NameServer + 1, // 40: management.FirewallRule.Direction:type_name -> management.RuleDirection + 2, // 41: management.FirewallRule.Action:type_name -> management.RuleAction + 0, // 42: management.FirewallRule.Protocol:type_name -> management.RuleProtocol + 40, // 43: management.FirewallRule.PortInfo:type_name -> management.PortInfo + 43, // 44: management.PortInfo.range:type_name -> management.PortInfo.Range + 2, // 45: management.RouteFirewallRule.action:type_name -> management.RuleAction + 0, // 46: management.RouteFirewallRule.protocol:type_name -> management.RuleProtocol + 40, // 47: management.RouteFirewallRule.portInfo:type_name -> management.PortInfo + 0, // 48: management.ForwardingRule.protocol:type_name -> management.RuleProtocol + 40, // 49: management.ForwardingRule.destinationPort:type_name -> management.PortInfo + 40, // 50: management.ForwardingRule.translatedPort:type_name -> management.PortInfo + 5, // 51: management.ManagementService.Login:input_type -> management.EncryptedMessage + 5, // 52: management.ManagementService.Sync:input_type -> management.EncryptedMessage + 17, // 53: management.ManagementService.GetServerKey:input_type -> management.Empty + 17, // 54: management.ManagementService.isHealthy:input_type -> management.Empty + 5, // 55: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage + 5, // 56: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage + 5, // 57: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage + 5, // 58: management.ManagementService.Login:output_type -> management.EncryptedMessage + 5, // 59: management.ManagementService.Sync:output_type -> management.EncryptedMessage + 16, // 60: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse + 17, // 61: management.ManagementService.isHealthy:output_type -> management.Empty + 5, // 62: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage + 5, // 63: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage + 17, // 64: management.ManagementService.SyncMeta:output_type -> management.Empty + 58, // [58:65] is the sub-list for method output_type + 51, // [51:58] is the sub-list for method input_type + 51, // [51:51] is the sub-list for extension type_name + 51, // [51:51] is the sub-list for extension extendee + 0, // [0:51] is the sub-list for field type_name } func init() { file_management_proto_init() } @@ -4092,6 +4201,18 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ForwardingRule); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PortInfo_Range); i { case 0: return &v.state @@ -4114,7 +4235,7 @@ func file_management_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_management_proto_rawDesc, NumEnums: 5, - NumMessages: 38, + NumMessages: 39, NumExtensions: 0, NumServices: 1, }, diff --git a/management/proto/management.proto b/management/proto/management.proto index cd207136f..863fa2c48 100644 --- a/management/proto/management.proto +++ b/management/proto/management.proto @@ -274,6 +274,8 @@ message NetworkMap { // RoutesFirewallRulesIsEmpty indicates whether RouteFirewallRule array is empty or not to bypass protobuf null and empty array equality. bool routesFirewallRulesIsEmpty = 11; + + repeated ForwardingRule forwardingRules = 12; } // RemotePeerConfig represents a configuration of a remote peer. @@ -483,3 +485,17 @@ message RouteFirewallRule { uint32 customProtocol = 8; } +message ForwardingRule { + // Protocol of the forwarding rule + RuleProtocol protocol = 1; + + // portInfo is the ingress destination port information, where the traffic arrives in the gateway node + PortInfo destinationPort = 2; + + // IP address of the translated address (remote peer) to send traffic to + // todo type pending + bytes translatedAddress = 3; + + // Translated port information, where the traffic should be forwarded to + PortInfo translatedPort = 4; +} diff --git a/management/server/account.go b/management/server/account.go index 332d356e2..80821ce91 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -27,7 +27,8 @@ import ( nbcontext "github.com/netbirdio/netbird/management/server/context" "github.com/netbirdio/netbird/management/server/geolocation" "github.com/netbirdio/netbird/management/server/idp" - "github.com/netbirdio/netbird/management/server/integrated_validator" + "github.com/netbirdio/netbird/management/server/integrations/integrated_validator" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/posture" "github.com/netbirdio/netbird/management/server/status" @@ -78,7 +79,7 @@ type AccountManager interface { GetUserByID(ctx context.Context, id string) (*types.User, error) GetUserFromUserAuth(ctx context.Context, userAuth nbcontext.UserAuth) (*types.User, error) ListUsers(ctx context.Context, accountID string) ([]*types.User, error) - GetPeers(ctx context.Context, accountID, userID string) ([]*nbpeer.Peer, error) + GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) MarkPeerConnected(ctx context.Context, peerKey string, connected bool, realIP net.IP, accountID string) error DeletePeer(ctx context.Context, accountID, peerID, userID string) error UpdatePeer(ctx context.Context, accountID, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, error) @@ -162,6 +163,8 @@ type DefaultAccountManager struct { requestBuffer *AccountRequestBuffer + proxyController port_forwarding.Controller + // singleAccountMode indicates whether the instance has a single account. // If true, then every new user will end up under the same account. // This value will be set to false if management service has more than one account. @@ -245,6 +248,7 @@ func BuildManager( userDeleteFromIDPEnabled bool, integratedPeerValidator integrated_validator.IntegratedValidator, metrics telemetry.AppMetrics, + proxyController port_forwarding.Controller, ) (*DefaultAccountManager, error) { start := time.Now() defer func() { @@ -267,6 +271,7 @@ func BuildManager( integratedPeerValidator: integratedPeerValidator, metrics: metrics, requestBuffer: NewAccountRequestBuffer(ctx, store), + proxyController: proxyController, } accountsCounter, err := store.GetAccountsCounter(ctx) if err != nil { diff --git a/management/server/account_test.go b/management/server/account_test.go index f203e2066..9a6828940 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/util" resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" @@ -2808,7 +2809,7 @@ func createManager(t TB) (*DefaultAccountManager, error) { return nil, err } - manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics) + manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock()) if err != nil { return nil, err } diff --git a/management/server/dns_test.go b/management/server/dns_test.go index c40f62324..3318dbaed 100644 --- a/management/server/dns_test.go +++ b/management/server/dns_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" "github.com/netbirdio/netbird/management/server/types" @@ -208,7 +209,7 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) { metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) require.NoError(t, err) - return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics) + return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock()) } func createDNSStore(t *testing.T) (store.Store, error) { diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 3d170afa4..9f77fd242 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -645,6 +645,14 @@ func toSyncResponse(ctx context.Context, config *Config, peer *nbpeer.Peer, turn response.NetworkMap.RoutesFirewallRules = routesFirewallRules response.NetworkMap.RoutesFirewallRulesIsEmpty = len(routesFirewallRules) == 0 + if networkMap.ForwardingRules != nil { + forwardingRules := make([]*proto.ForwardingRule, 0, len(networkMap.ForwardingRules)) + for _, rule := range networkMap.ForwardingRules { + forwardingRules = append(forwardingRules, rule.ToProto()) + } + response.NetworkMap.ForwardingRules = forwardingRules + } + return response } diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 83f45ef91..ab4f7c0ba 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -29,6 +29,9 @@ tags: description: View information about the account and network events. - name: Accounts description: View information about the accounts. + - name: Ingress Ports + description: Interact with and view information about the ingress peers and ports. + x-cloud-only: true components: schemas: Account: @@ -1597,6 +1600,223 @@ components: - initiator_email - target_id - meta + IngressPeerCreateRequest: + type: object + properties: + peer_id: + description: ID of the peer that is used as an ingress peer + type: string + example: ch8i4ug6lnn4g9hqv7m0 + enabled: + description: Defines if an ingress peer is enabled + type: boolean + example: true + fallback: + description: Defines if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer + type: boolean + example: true + required: + - peer_id + - enabled + - fallback + IngressPeerUpdateRequest: + type: object + properties: + enabled: + description: Defines if an ingress peer is enabled + type: boolean + example: true + fallback: + description: Defines if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer + type: boolean + example: true + required: + - enabled + - fallback + IngressPeer: + type: object + properties: + id: + description: ID of the ingress peer + type: string + example: ch8i4ug6lnn4g9hqv7m0 + peer_id: + description: ID of the peer that is used as an ingress peer + type: string + example: x7p3kqf2rdd8j5zxw4n9 + ingress_ip: + description: Ingress IP address of the ingress peer where the traffic arrives + type: string + example: 192.34.0.123 + available_ports: + $ref: '#/components/schemas/AvailablePorts' + enabled: + description: Indicates if an ingress peer is enabled + type: boolean + example: true + connected: + description: Indicates if an ingress peer is connected to the management server + type: boolean + example: true + fallback: + description: Indicates if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer + type: boolean + example: true + region: + description: Region of the ingress peer + type: string + example: germany + required: + - id + - peer_id + - ingress_ip + - available_ports + - enabled + - connected + - fallback + - region + + AvailablePorts: + type: object + properties: + tcp: + description: Number of available TCP ports left on the ingress peer + type: integer + example: 45765 + udp: + description: Number of available UDP ports left on the ingress peer + type: integer + example: 50000 + required: + - tcp + - udp + IngressPortAllocationRequest: + type: object + properties: + name: + description: Name of the ingress port allocation + type: string + example: Ingress Port Allocation 1 + enabled: + description: Indicates if an ingress port allocation is enabled + type: boolean + example: true + port_ranges: + description: List of port ranges that are forwarded by the ingress peer + type: array + items: + $ref: '#/components/schemas/IngressPortAllocationRequestPortRange' + direct_port: + description: Direct port allocation + $ref: '#/components/schemas/IngressPortAllocationRequestDirectPort' + required: + - name + - enabled + IngressPortAllocationRequestPortRange: + type: object + properties: + start: + description: The starting port of the range of forwarded ports + type: integer + example: 80 + end: + description: The ending port of the range of forwarded ports + type: integer + example: 320 + protocol: + description: The protocol accepted by the port range + type: string + enum: [ "tcp", "udp", "tcp/udp" ] + example: tcp + required: + - start + - end + - protocol + IngressPortAllocationRequestDirectPort: + type: object + properties: + count: + description: The number of ports to be forwarded + type: integer + example: 5 + protocol: + description: The protocol accepted by the port + type: string + enum: [ "tcp", "udp", "tcp/udp" ] + example: udp + required: + - count + - protocol + IngressPortAllocation: + type: object + properties: + id: + description: ID of the ingress port allocation + type: string + example: ch8i4ug6lnn4g9hqv7m0 + name: + description: Name of the ingress port allocation + type: string + example: Ingress Peer Allocation 1 + ingress_peer_id: + description: ID of the ingress peer that forwards the ports + type: string + example: x7p3kqf2rdd8j5zxw4n9 + region: + description: Region of the ingress peer + type: string + example: germany + enabled: + description: Indicates if an ingress port allocation is enabled + type: boolean + example: true + ingress_ip: + description: Ingress IP address of the ingress peer where the traffic arrives + type: string + example: + port_range_mappings: + description: List of port ranges that are allowed to be used by the ingress peer + type: array + items: + $ref: '#/components/schemas/IngressPortAllocationPortMapping' + required: + - id + - name + - ingress_peer_id + - region + - enabled + - ingress_ip + - port_range_mappings + IngressPortAllocationPortMapping: + type: object + properties: + translated_start: + description: The starting port of the translated range of forwarded ports + type: integer + example: 80 + translated_end: + description: The ending port of the translated range of forwarded ports + type: integer + example: 320 + ingress_start: + description: The starting port of the range of ingress ports mapped to the forwarded ports + type: integer + example: 1080 + ingress_end: + description: The ending port of the range of ingress ports mapped to the forwarded ports + type: integer + example: 1320 + protocol: + description: Protocol accepted by the ports + type: string + enum: [ "tcp", "udp", "tcp/udp" ] + example: tcp + required: + - translated_start + - translated_end + - ingress_start + - ingress_end + - protocol responses: not_found: description: Resource not found @@ -2009,6 +2229,17 @@ paths: summary: List all Peers description: Returns a list of all peers tags: [ Peers ] + parameters: + - in: query + name: name + schema: + type: string + description: Filter peers by name + - in: query + name: ip + schema: + type: string + description: Filter peers by IP address security: - BearerAuth: [ ] - TokenAuth: [ ] @@ -2152,6 +2383,335 @@ paths: "$ref": "#/components/responses/forbidden" '500': "$ref": "#/components/responses/internal_error" + /api/peers/{peerId}/ingress/ports: + get: + x-cloud-only: true + summary: List all Ingress Port Allocations for a Peer + description: Returns a list of all ingress port allocations for a peer + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + - in: query + name: name + schema: + type: string + description: Filters ingress port allocations by name + responses: + '200': + description: A JSON Array of Ingress Port Allocations + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IngressPortAllocation' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + x-cloud-only: true + summary: Create a Ingress Port Allocation + description: Creates a new ingress port allocation for a peer + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + requestBody: + description: New Ingress Port Allocation request + content: + 'application/json': + schema: + $ref: '#/components/schemas/IngressPortAllocationRequest' + responses: + '200': + description: A Ingress Port Allocation object + content: + application/json: + schema: + $ref: '#/components/schemas/IngressPortAllocation' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/peers/{peerId}/ingress/ports/{allocationId}: + get: + x-cloud-only: true + summary: Retrieve a Ingress Port Allocation + description: Get information about an ingress port allocation + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + - in: path + name: allocationId + required: true + schema: + type: string + description: The unique identifier of an ingress port allocation + responses: + '200': + description: A Ingress Port Allocation object + content: + application/json: + schema: + $ref: '#/components/schemas/IngressPortAllocation' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + x-cloud-only: true + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + - in: path + name: allocationId + required: true + schema: + type: string + description: The unique identifier of an ingress port allocation + requestBody: + description: update an ingress port allocation + content: + application/json: + schema: + $ref: '#/components/schemas/IngressPortAllocationRequest' + responses: + '200': + description: A Ingress Port Allocation object + content: + application/json: + schema: + $ref: '#/components/schemas/IngressPortAllocation' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + x-cloud-only: true + summary: Delete a Ingress Port Allocation + description: Delete an ingress port allocation + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + - in: path + name: allocationId + required: true + schema: + type: string + description: The unique identifier of an ingress port allocation + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/ingress/peers: + get: + x-cloud-only: true + summary: List all Ingress Peers + description: Returns a list of all ingress peers + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Ingress Peers + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IngressPeer' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + x-cloud-only: true + summary: Create a Ingress Peer + description: Creates a new ingress peer + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Ingress Peer request + content: + 'application/json': + schema: + $ref: '#/components/schemas/IngressPeerCreateRequest' + responses: + '200': + description: A Ingress Peer object + content: + application/json: + schema: + $ref: '#/components/schemas/IngressPeer' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/ingress/peers/{ingressPeerId}: + get: + x-cloud-only: true + summary: Retrieve a Ingress Peer + description: Get information about an ingress peer + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: ingressPeerId + required: true + schema: + type: string + description: The unique identifier of an ingress peer + responses: + '200': + description: A Ingress Peer object + content: + application/json: + schema: + $ref: '#/components/schemas/IngressPeer' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + x-cloud-only: true + summary: Update a Ingress Peer + description: Update information about an ingress peer + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: ingressPeerId + required: true + schema: + type: string + description: The unique identifier of an ingress peer + requestBody: + description: update an ingress peer + content: + 'application/json': + schema: + $ref: '#/components/schemas/IngressPeerUpdateRequest' + responses: + '200': + description: A Ingress Peer object + content: + application/json: + schema: + $ref: '#/components/schemas/IngressPeer' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + x-cloud-only: true + summary: Delete a Ingress Peer + description: Delete an ingress peer + tags: [ Ingress Ports ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: ingressPeerId + required: true + schema: + type: string + description: The unique identifier of an ingress peer + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" /api/setup-keys: get: summary: List all Setup Keys diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index eb57d5d66..fc5d3d707 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -83,6 +83,27 @@ const ( GroupMinimumIssuedJwt GroupMinimumIssued = "jwt" ) +// Defines values for IngressPortAllocationPortMappingProtocol. +const ( + IngressPortAllocationPortMappingProtocolTcp IngressPortAllocationPortMappingProtocol = "tcp" + IngressPortAllocationPortMappingProtocolTcpudp IngressPortAllocationPortMappingProtocol = "tcp/udp" + IngressPortAllocationPortMappingProtocolUdp IngressPortAllocationPortMappingProtocol = "udp" +) + +// Defines values for IngressPortAllocationRequestDirectPortProtocol. +const ( + IngressPortAllocationRequestDirectPortProtocolTcp IngressPortAllocationRequestDirectPortProtocol = "tcp" + IngressPortAllocationRequestDirectPortProtocolTcpudp IngressPortAllocationRequestDirectPortProtocol = "tcp/udp" + IngressPortAllocationRequestDirectPortProtocolUdp IngressPortAllocationRequestDirectPortProtocol = "udp" +) + +// Defines values for IngressPortAllocationRequestPortRangeProtocol. +const ( + IngressPortAllocationRequestPortRangeProtocolTcp IngressPortAllocationRequestPortRangeProtocol = "tcp" + IngressPortAllocationRequestPortRangeProtocolTcpudp IngressPortAllocationRequestPortRangeProtocol = "tcp/udp" + IngressPortAllocationRequestPortRangeProtocolUdp IngressPortAllocationRequestPortRangeProtocol = "udp" +) + // Defines values for NameserverNsType. const ( NameserverNsTypeUdp NameserverNsType = "udp" @@ -253,6 +274,15 @@ type AccountSettings struct { RoutingPeerDnsResolutionEnabled *bool `json:"routing_peer_dns_resolution_enabled,omitempty"` } +// AvailablePorts defines model for AvailablePorts. +type AvailablePorts struct { + // Tcp Number of available TCP ports left on the ingress peer + Tcp int `json:"tcp"` + + // Udp Number of available UDP ports left on the ingress peer + Udp int `json:"udp"` +} + // Checks List of objects that perform the actual checks type Checks struct { // GeoLocationCheck Posture check for geo location @@ -426,6 +456,139 @@ type GroupRequest struct { Resources *[]Resource `json:"resources,omitempty"` } +// IngressPeer defines model for IngressPeer. +type IngressPeer struct { + AvailablePorts AvailablePorts `json:"available_ports"` + + // Connected Indicates if an ingress peer is connected to the management server + Connected bool `json:"connected"` + + // Enabled Indicates if an ingress peer is enabled + Enabled bool `json:"enabled"` + + // Fallback Indicates if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer + Fallback bool `json:"fallback"` + + // Id ID of the ingress peer + Id string `json:"id"` + + // IngressIp Ingress IP address of the ingress peer where the traffic arrives + IngressIp string `json:"ingress_ip"` + + // PeerId ID of the peer that is used as an ingress peer + PeerId string `json:"peer_id"` + + // Region Region of the ingress peer + Region string `json:"region"` +} + +// IngressPeerCreateRequest defines model for IngressPeerCreateRequest. +type IngressPeerCreateRequest struct { + // Enabled Defines if an ingress peer is enabled + Enabled bool `json:"enabled"` + + // Fallback Defines if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer + Fallback bool `json:"fallback"` + + // PeerId ID of the peer that is used as an ingress peer + PeerId string `json:"peer_id"` +} + +// IngressPeerUpdateRequest defines model for IngressPeerUpdateRequest. +type IngressPeerUpdateRequest struct { + // Enabled Defines if an ingress peer is enabled + Enabled bool `json:"enabled"` + + // Fallback Defines if an ingress peer can be used as a fallback if no ingress peer can be found in the region of the forwarded peer + Fallback bool `json:"fallback"` +} + +// IngressPortAllocation defines model for IngressPortAllocation. +type IngressPortAllocation struct { + // Enabled Indicates if an ingress port allocation is enabled + Enabled bool `json:"enabled"` + + // Id ID of the ingress port allocation + Id string `json:"id"` + + // IngressIp Ingress IP address of the ingress peer where the traffic arrives + IngressIp string `json:"ingress_ip"` + + // IngressPeerId ID of the ingress peer that forwards the ports + IngressPeerId string `json:"ingress_peer_id"` + + // Name Name of the ingress port allocation + Name string `json:"name"` + + // PortRangeMappings List of port ranges that are allowed to be used by the ingress peer + PortRangeMappings []IngressPortAllocationPortMapping `json:"port_range_mappings"` + + // Region Region of the ingress peer + Region string `json:"region"` +} + +// IngressPortAllocationPortMapping defines model for IngressPortAllocationPortMapping. +type IngressPortAllocationPortMapping struct { + // IngressEnd The ending port of the range of ingress ports mapped to the forwarded ports + IngressEnd int `json:"ingress_end"` + + // IngressStart The starting port of the range of ingress ports mapped to the forwarded ports + IngressStart int `json:"ingress_start"` + + // Protocol Protocol accepted by the ports + Protocol IngressPortAllocationPortMappingProtocol `json:"protocol"` + + // TranslatedEnd The ending port of the translated range of forwarded ports + TranslatedEnd int `json:"translated_end"` + + // TranslatedStart The starting port of the translated range of forwarded ports + TranslatedStart int `json:"translated_start"` +} + +// IngressPortAllocationPortMappingProtocol Protocol accepted by the ports +type IngressPortAllocationPortMappingProtocol string + +// IngressPortAllocationRequest defines model for IngressPortAllocationRequest. +type IngressPortAllocationRequest struct { + DirectPort *IngressPortAllocationRequestDirectPort `json:"direct_port,omitempty"` + + // Enabled Indicates if an ingress port allocation is enabled + Enabled bool `json:"enabled"` + + // Name Name of the ingress port allocation + Name string `json:"name"` + + // PortRanges List of port ranges that are forwarded by the ingress peer + PortRanges *[]IngressPortAllocationRequestPortRange `json:"port_ranges,omitempty"` +} + +// IngressPortAllocationRequestDirectPort defines model for IngressPortAllocationRequestDirectPort. +type IngressPortAllocationRequestDirectPort struct { + // Count The number of ports to be forwarded + Count int `json:"count"` + + // Protocol The protocol accepted by the port + Protocol IngressPortAllocationRequestDirectPortProtocol `json:"protocol"` +} + +// IngressPortAllocationRequestDirectPortProtocol The protocol accepted by the port +type IngressPortAllocationRequestDirectPortProtocol string + +// IngressPortAllocationRequestPortRange defines model for IngressPortAllocationRequestPortRange. +type IngressPortAllocationRequestPortRange struct { + // End The ending port of the range of forwarded ports + End int `json:"end"` + + // Protocol The protocol accepted by the port range + Protocol IngressPortAllocationRequestPortRangeProtocol `json:"protocol"` + + // Start The starting port of the range of forwarded ports + Start int `json:"start"` +} + +// IngressPortAllocationRequestPortRangeProtocol The protocol accepted by the port range +type IngressPortAllocationRequestPortRangeProtocol string + // Location Describe geographical location information type Location struct { // CityName Commonly used English name of the city @@ -1466,6 +1629,21 @@ type UserRequest struct { Role string `json:"role"` } +// GetApiPeersParams defines parameters for GetApiPeers. +type GetApiPeersParams struct { + // Name Filter peers by name + Name *string `form:"name,omitempty" json:"name,omitempty"` + + // Ip Filter peers by IP address + Ip *string `form:"ip,omitempty" json:"ip,omitempty"` +} + +// GetApiPeersPeerIdIngressPortsParams defines parameters for GetApiPeersPeerIdIngressPorts. +type GetApiPeersPeerIdIngressPortsParams struct { + // Name Filters ingress port allocations by name + Name *string `form:"name,omitempty" json:"name,omitempty"` +} + // GetApiUsersParams defines parameters for GetApiUsers. type GetApiUsersParams struct { // ServiceUser Filters users and returns either regular users or service users @@ -1490,6 +1668,12 @@ type PostApiGroupsJSONRequestBody = GroupRequest // PutApiGroupsGroupIdJSONRequestBody defines body for PutApiGroupsGroupId for application/json ContentType. type PutApiGroupsGroupIdJSONRequestBody = GroupRequest +// PostApiIngressPeersJSONRequestBody defines body for PostApiIngressPeers for application/json ContentType. +type PostApiIngressPeersJSONRequestBody = IngressPeerCreateRequest + +// PutApiIngressPeersIngressPeerIdJSONRequestBody defines body for PutApiIngressPeersIngressPeerId for application/json ContentType. +type PutApiIngressPeersIngressPeerIdJSONRequestBody = IngressPeerUpdateRequest + // PostApiNetworksJSONRequestBody defines body for PostApiNetworks for application/json ContentType. type PostApiNetworksJSONRequestBody = NetworkRequest @@ -1511,6 +1695,12 @@ type PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody = NetworkRouterReques // PutApiPeersPeerIdJSONRequestBody defines body for PutApiPeersPeerId for application/json ContentType. type PutApiPeersPeerIdJSONRequestBody = PeerRequest +// PostApiPeersPeerIdIngressPortsJSONRequestBody defines body for PostApiPeersPeerIdIngressPorts for application/json ContentType. +type PostApiPeersPeerIdIngressPortsJSONRequestBody = IngressPortAllocationRequest + +// PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody defines body for PutApiPeersPeerIdIngressPortsAllocationId for application/json ContentType. +type PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody = IngressPortAllocationRequest + // PostApiPoliciesJSONRequestBody defines body for PostApiPolicies for application/json ContentType. type PostApiPoliciesJSONRequestBody = PolicyUpdate diff --git a/management/server/http/handler.go b/management/server/http/handler.go index 2b87c5f25..f4c4bc763 100644 --- a/management/server/http/handler.go +++ b/management/server/http/handler.go @@ -10,6 +10,9 @@ import ( "github.com/netbirdio/management-integrations/integrations" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" + "github.com/netbirdio/netbird/management/server/permissions" + s "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/auth" "github.com/netbirdio/netbird/management/server/geolocation" @@ -25,10 +28,11 @@ import ( "github.com/netbirdio/netbird/management/server/http/handlers/setup_keys" "github.com/netbirdio/netbird/management/server/http/handlers/users" "github.com/netbirdio/netbird/management/server/http/middleware" - "github.com/netbirdio/netbird/management/server/integrated_validator" + "github.com/netbirdio/netbird/management/server/integrations/integrated_validator" nbnetworks "github.com/netbirdio/netbird/management/server/networks" "github.com/netbirdio/netbird/management/server/networks/resources" "github.com/netbirdio/netbird/management/server/networks/routers" + nbpeers "github.com/netbirdio/netbird/management/server/peers" "github.com/netbirdio/netbird/management/server/telemetry" ) @@ -45,8 +49,11 @@ func NewAPIHandler( LocationManager geolocation.Geolocation, authManager auth.Manager, appMetrics telemetry.AppMetrics, - config *s.Config, - integratedValidator integrated_validator.IntegratedValidator) (http.Handler, error) { + integratedValidator integrated_validator.IntegratedValidator, + proxyController port_forwarding.Controller, + permissionsManager permissions.Manager, + peersManager nbpeers.Manager, +) (http.Handler, error) { authMiddleware := middleware.NewAuthMiddleware( authManager, @@ -66,7 +73,7 @@ func NewAPIHandler( router.Use(metricsMiddleware.Handler, corsMiddleware.Handler, authMiddleware.Handler, acMiddleware.Handler) - if _, err := integrations.RegisterHandlers(ctx, prefix, router, accountManager, integratedValidator, appMetrics.GetMeter()); err != nil { + if _, err := integrations.RegisterHandlers(ctx, prefix, router, accountManager, integratedValidator, appMetrics.GetMeter(), permissionsManager, peersManager, proxyController); err != nil { return nil, fmt.Errorf("register integrations endpoints: %w", err) } diff --git a/management/server/http/handlers/groups/groups_handler.go b/management/server/http/handlers/groups/groups_handler.go index 040c08b87..2d0b8bdbd 100644 --- a/management/server/http/handlers/groups/groups_handler.go +++ b/management/server/http/handlers/groups/groups_handler.go @@ -54,7 +54,7 @@ func (h *handler) getAllGroups(w http.ResponseWriter, r *http.Request) { return } - accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID) + accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "") if err != nil { util.WriteError(r.Context(), err, w) return @@ -149,7 +149,7 @@ func (h *handler) updateGroup(w http.ResponseWriter, r *http.Request) { return } - accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID) + accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "") if err != nil { util.WriteError(r.Context(), err, w) return @@ -209,7 +209,7 @@ func (h *handler) createGroup(w http.ResponseWriter, r *http.Request) { return } - accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID) + accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "") if err != nil { util.WriteError(r.Context(), err, w) return @@ -270,7 +270,7 @@ func (h *handler) getGroup(w http.ResponseWriter, r *http.Request) { return } - accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID) + accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "") if err != nil { util.WriteError(r.Context(), err, w) return diff --git a/management/server/http/handlers/groups/groups_handler_test.go b/management/server/http/handlers/groups/groups_handler_test.go index c4b9e46ab..f4ac34e53 100644 --- a/management/server/http/handlers/groups/groups_handler_test.go +++ b/management/server/http/handlers/groups/groups_handler_test.go @@ -66,7 +66,7 @@ func initGroupTestData(initGroups ...*types.Group) *handler { return nil, fmt.Errorf("unknown group name") }, - GetPeersFunc: func(ctx context.Context, accountID, userID string) ([]*nbpeer.Peer, error) { + GetPeersFunc: func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) { return maps.Values(TestPeers), nil }, DeleteGroupFunc: func(_ context.Context, accountID, userId, groupID string) error { diff --git a/management/server/http/handlers/peers/peers_handler.go b/management/server/http/handlers/peers/peers_handler.go index 709ba64d0..2336d16cf 100644 --- a/management/server/http/handlers/peers/peers_handler.go +++ b/management/server/http/handlers/peers/peers_handler.go @@ -180,9 +180,12 @@ func (h *Handler) GetAllPeers(w http.ResponseWriter, r *http.Request) { return } + nameFilter := r.URL.Query().Get("name") + ipFilter := r.URL.Query().Get("ip") + accountID, userID := userAuth.AccountId, userAuth.UserId - peers, err := h.accountManager.GetPeers(r.Context(), accountID, userID) + peers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, nameFilter, ipFilter) if err != nil { util.WriteError(r.Context(), err, w) return diff --git a/management/server/http/handlers/peers/peers_handler_test.go b/management/server/http/handlers/peers/peers_handler_test.go index 63b8c0ab3..cb60ae4f1 100644 --- a/management/server/http/handlers/peers/peers_handler_test.go +++ b/management/server/http/handlers/peers/peers_handler_test.go @@ -122,7 +122,7 @@ func initTestMetaData(peers ...*nbpeer.Peer) *Handler { } return p, nil }, - GetPeersFunc: func(_ context.Context, accountID, userID string) ([]*nbpeer.Peer, error) { + GetPeersFunc: func(_ context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) { return peers, nil }, GetPeerGroupsFunc: func(ctx context.Context, accountID, peerID string) ([]*types.Group, error) { diff --git a/management/server/http/testing/testing_tools/tools.go b/management/server/http/testing/testing_tools/tools.go index e534dac46..a74299fa0 100644 --- a/management/server/http/testing/testing_tools/tools.go +++ b/management/server/http/testing/testing_tools/tools.go @@ -15,9 +15,13 @@ import ( "time" "github.com/golang-jwt/jwt" + "github.com/netbirdio/management-integrations/integrations" "github.com/stretchr/testify/assert" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + "github.com/netbirdio/netbird/management/server/peers" + "github.com/netbirdio/netbird/management/server/permissions" + "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/auth" @@ -112,7 +116,8 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve geoMock := &geolocation.Mock{} validatorMock := server.MocIntegratedValidator{} - am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics) + proxyController := integrations.NewController(store) + am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController) if err != nil { t.Fatalf("Failed to create manager: %v", err) } @@ -130,7 +135,10 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve resourcesManagerMock := resources.NewManagerMock() routersManagerMock := routers.NewManagerMock() groupsManagerMock := groups.NewManagerMock() - apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, &server.Config{}, validatorMock) + permissionsManagerMock := permissions.NewManagerMock() + peersManager := peers.NewManager(store, permissionsManagerMock) + + apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManagerMock, peersManager) if err != nil { t.Fatalf("Failed to create API handler: %v", err) } diff --git a/management/server/integrated_validator.go b/management/server/integrated_validator.go index b9827f457..b95ea1699 100644 --- a/management/server/integrated_validator.go +++ b/management/server/integrated_validator.go @@ -88,7 +88,7 @@ func (am *DefaultAccountManager) GetValidatedPeers(ctx context.Context, accountI return err } - peers, err = transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID) + peers, err = transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "") return err }) if err != nil { diff --git a/management/server/integrated_validator/interface.go b/management/server/integrations/integrated_validator/interface.go similarity index 100% rename from management/server/integrated_validator/interface.go rename to management/server/integrations/integrated_validator/interface.go diff --git a/management/server/integrations/port_forwarding/controller.go b/management/server/integrations/port_forwarding/controller.go new file mode 100644 index 000000000..6f062bb12 --- /dev/null +++ b/management/server/integrations/port_forwarding/controller.go @@ -0,0 +1,32 @@ +package port_forwarding + +import ( + "context" + + nbtypes "github.com/netbirdio/netbird/management/server/types" +) + +type Controller interface { + SendUpdate(ctx context.Context, accountID string, affectedProxyID string, affectedPeerIDs []string) + GetProxyNetworkMaps(ctx context.Context, accountID string) (map[string]*nbtypes.NetworkMap, error) + IsPeerInIngressPorts(ctx context.Context, accountID, peerID string) (bool, error) +} + +type ControllerMock struct { +} + +func NewControllerMock() *ControllerMock { + return &ControllerMock{} +} + +func (c *ControllerMock) SendUpdate(ctx context.Context, accountID string, affectedProxyID string, affectedPeerIDs []string) { + // noop +} + +func (c *ControllerMock) GetProxyNetworkMaps(ctx context.Context, accountID string) (map[string]*nbtypes.NetworkMap, error) { + return make(map[string]*nbtypes.NetworkMap), nil +} + +func (c *ControllerMock) IsPeerInIngressPorts(ctx context.Context, accountID, peerID string) (bool, error) { + return false, nil +} diff --git a/management/server/management_proto_test.go b/management/server/management_proto_test.go index 28f5abf57..74ec20cf4 100644 --- a/management/server/management_proto_test.go +++ b/management/server/management_proto_test.go @@ -23,6 +23,7 @@ import ( "github.com/netbirdio/netbird/formatter/hook" mgmtProto "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/settings" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" @@ -430,7 +431,7 @@ func startManagementForTest(t *testing.T, testFile string, config *Config) (*grp require.NoError(t, err) accountManager, err := BuildManager(ctx, store, peersUpdateManager, nil, "", "netbird.selfhosted", - eventStore, nil, false, MocIntegratedValidator{}, metrics) + eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock()) if err != nil { cleanup() diff --git a/management/server/management_test.go b/management/server/management_test.go index fd82d8037..838065e49 100644 --- a/management/server/management_test.go +++ b/management/server/management_test.go @@ -22,6 +22,7 @@ import ( mgmtProto "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/settings" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" @@ -189,6 +190,7 @@ func startServer( false, server.MocIntegratedValidator{}, metrics, + port_forwarding.NewControllerMock(), ) if err != nil { t.Fatalf("failed creating an account manager: %v", err) diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index 67c23b95d..5564aab01 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -33,7 +33,7 @@ type MockAccountManager struct { GetAccountIDByUserIdFunc func(ctx context.Context, userId, domain string) (string, error) GetUserFromUserAuthFunc func(ctx context.Context, userAuth nbcontext.UserAuth) (*types.User, error) ListUsersFunc func(ctx context.Context, accountID string) ([]*types.User, error) - GetPeersFunc func(ctx context.Context, accountID, userID string) ([]*nbpeer.Peer, error) + GetPeersFunc func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) MarkPeerConnectedFunc func(ctx context.Context, peerKey string, connected bool, realIP net.IP) error SyncAndMarkPeerFunc func(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) DeletePeerFunc func(ctx context.Context, accountID, peerKey, userID string) error @@ -605,9 +605,9 @@ func (am *MockAccountManager) GetAccountIDFromUserAuth(ctx context.Context, user } // GetPeers mocks GetPeers of the AccountManager interface -func (am *MockAccountManager) GetPeers(ctx context.Context, accountID, userID string) ([]*nbpeer.Peer, error) { +func (am *MockAccountManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) { if am.GetPeersFunc != nil { - return am.GetPeersFunc(ctx, accountID, userID) + return am.GetPeersFunc(ctx, accountID, userID, nameFilter, ipFilter) } return nil, status.Errorf(codes.Unimplemented, "method GetPeers is not implemented") } diff --git a/management/server/nameserver_test.go b/management/server/nameserver_test.go index 497d9af4f..064a645d7 100644 --- a/management/server/nameserver_test.go +++ b/management/server/nameserver_test.go @@ -11,6 +11,7 @@ import ( nbdns "github.com/netbirdio/netbird/dns" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" @@ -771,7 +772,7 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) { metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) require.NoError(t, err) - return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics) + return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock()) } func createNSStore(t *testing.T) (store.Store, error) { diff --git a/management/server/peer.go b/management/server/peer.go index ff898f56c..60d3fee38 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -61,7 +61,7 @@ type PeerLogin struct { // GetPeers returns a list of peers under the given account filtering out peers that do not belong to a user if // the current user is not an admin. -func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID string) ([]*nbpeer.Peer, error) { +func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) { user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID) if err != nil { return nil, err @@ -80,7 +80,7 @@ func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID return []*nbpeer.Peer{}, nil } - accountPeers, err := am.Store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID) + accountPeers, err := am.Store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, nameFilter, ipFilter) if err != nil { return nil, err } @@ -375,6 +375,10 @@ func (am *DefaultAccountManager) DeletePeer(ctx context.Context, accountID, peer return err } + if err = am.validatePeerDelete(ctx, accountID, peerID); err != nil { + return err + } + updateAccountPeers, err = isPeerInActiveGroup(ctx, transaction, accountID, peerID) if err != nil { return err @@ -437,7 +441,21 @@ func (am *DefaultAccountManager) GetNetworkMap(ctx context.Context, peerID strin return nil, err } customZone := account.GetPeersCustomZone(ctx, am.dnsDomain) - return account.GetPeerNetworkMap(ctx, peer.ID, customZone, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil), nil + + proxyNetworkMaps, err := am.proxyController.GetProxyNetworkMaps(ctx, account.Id) + if err != nil { + log.WithContext(ctx).Errorf("failed to get proxy network maps: %v", err) + return nil, err + } + + networkMap := account.GetPeerNetworkMap(ctx, peer.ID, customZone, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil) + + proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] + if ok { + networkMap.Merge(proxyNetworkMap) + } + + return networkMap, nil } // GetPeerNetwork returns the Network for a given peer @@ -1034,7 +1052,21 @@ func (am *DefaultAccountManager) getValidatedPeerWithMap(ctx context.Context, is } customZone := account.GetPeersCustomZone(ctx, am.dnsDomain) - return peer, account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), am.metrics.AccountManagerMetrics()), postureChecks, nil + + proxyNetworkMaps, err := am.proxyController.GetProxyNetworkMaps(ctx, account.Id) + if err != nil { + log.WithContext(ctx).Errorf("failed to get proxy network maps: %v", err) + return nil, nil, nil, err + } + + networkMap := account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), am.metrics.AccountManagerMetrics()) + + proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] + if ok { + networkMap.Merge(proxyNetworkMap) + } + + return peer, networkMap, postureChecks, nil } func (am *DefaultAccountManager) handleExpiredPeer(ctx context.Context, transaction store.Store, user *types.User, peer *nbpeer.Peer) error { @@ -1174,6 +1206,12 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account resourcePolicies := account.GetResourcePoliciesMap() routers := account.GetResourceRoutersMap() + proxyNetworkMaps, err := am.proxyController.GetProxyNetworkMaps(ctx, accountID) + if err != nil { + log.WithContext(ctx).Errorf("failed to get proxy network maps: %v", err) + return + } + for _, peer := range account.Peers { if !am.peersUpdateManager.HasChannel(peer.ID) { log.WithContext(ctx).Tracef("peer %s doesn't have a channel, skipping network map update", peer.ID) @@ -1193,11 +1231,19 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account } remotePeerNetworkMap := account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics()) + + proxyNetworkMap, ok := proxyNetworkMaps[p.ID] + if ok { + remotePeerNetworkMap.Merge(proxyNetworkMap) + } + update := toSyncResponse(ctx, nil, p, nil, nil, remotePeerNetworkMap, am.GetDNSDomain(), postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled) am.peersUpdateManager.SendUpdate(ctx, p.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap}) }(peer) } + // + wg.Wait() if am.metrics != nil { am.metrics.AccountManagerMetrics().CountUpdateAccountPeersDuration(time.Since(start)) @@ -1241,7 +1287,19 @@ func (am *DefaultAccountManager) UpdateAccountPeer(ctx context.Context, accountI return } + proxyNetworkMaps, err := am.proxyController.GetProxyNetworkMaps(ctx, accountId) + if err != nil { + log.WithContext(ctx).Errorf("failed to get proxy network maps: %v", err) + return + } + remotePeerNetworkMap := account.GetPeerNetworkMap(ctx, peerId, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics()) + + proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] + if ok { + remotePeerNetworkMap.Merge(proxyNetworkMap) + } + update := toSyncResponse(ctx, nil, peer, nil, nil, remotePeerNetworkMap, am.GetDNSDomain(), postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled) am.peersUpdateManager.SendUpdate(ctx, peer.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap}) } @@ -1471,3 +1529,17 @@ func ConvertSliceToMap(existingLabels []string) map[string]struct{} { } return labelMap } + +// validatePeerDelete checks if the peer can be deleted. +func (am *DefaultAccountManager) validatePeerDelete(ctx context.Context, accountId, peerId string) error { + linkedInIngressPorts, err := am.proxyController.IsPeerInIngressPorts(ctx, accountId, peerId) + if err != nil { + return err + } + + if linkedInIngressPorts { + return status.Errorf(status.PreconditionFailed, "peer is linked to ingress ports: %s", peerId) + } + + return nil +} diff --git a/management/server/peer_test.go b/management/server/peer_test.go index 9deb8e456..843910597 100644 --- a/management/server/peer_test.go +++ b/management/server/peer_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/require" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/util" resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" @@ -707,7 +708,7 @@ func TestDefaultAccountManager_GetPeers(t *testing.T) { return } - peers, err := manager.GetPeers(context.Background(), accountID, someUser) + peers, err := manager.GetPeers(context.Background(), accountID, someUser, "", "") if err != nil { t.Fatal(err) return @@ -913,7 +914,7 @@ func BenchmarkGetPeers(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := manager.GetPeers(context.Background(), accountID, userID) + _, err := manager.GetPeers(context.Background(), accountID, userID, "", "") if err != nil { b.Fatalf("GetPeers failed: %v", err) } @@ -1079,6 +1080,20 @@ func TestToSyncResponse(t *testing.T) { FirewallRules: []*types.FirewallRule{ {PeerIP: "192.168.1.2", Direction: types.FirewallRuleDirectionIN, Action: string(types.PolicyTrafficActionAccept), Protocol: string(types.PolicyRuleProtocolTCP), Port: "80"}, }, + ForwardingRules: []*types.ForwardingRule{ + { + RuleProtocol: "tcp", + DestinationPorts: types.RulePortRange{ + Start: 1000, + End: 2000, + }, + TranslatedAddress: net.IPv4(192, 168, 1, 2), + TranslatedPorts: types.RulePortRange{ + Start: 11000, + End: 12000, + }, + }, + }, } dnsName := "example.com" checks := []*posture.Checks{ @@ -1170,6 +1185,14 @@ func TestToSyncResponse(t *testing.T) { // assert posture checks assert.Equal(t, 1, len(response.Checks)) assert.Equal(t, "/usr/bin/netbird", response.Checks[0].Files[0]) + // assert network map ForwardingRules + assert.Equal(t, 1, len(response.NetworkMap.ForwardingRules)) + assert.Equal(t, proto.RuleProtocol_TCP, response.NetworkMap.ForwardingRules[0].Protocol) + assert.Equal(t, uint32(1000), response.NetworkMap.ForwardingRules[0].DestinationPort.GetRange().Start) + assert.Equal(t, uint32(2000), response.NetworkMap.ForwardingRules[0].DestinationPort.GetRange().End) + assert.Equal(t, net.IPv4(192, 168, 1, 2).To4(), net.IP(response.NetworkMap.ForwardingRules[0].TranslatedAddress)) + assert.Equal(t, uint32(11000), response.NetworkMap.ForwardingRules[0].TranslatedPort.GetRange().Start) + assert.Equal(t, uint32(12000), response.NetworkMap.ForwardingRules[0].TranslatedPort.GetRange().End) } func Test_RegisterPeerByUser(t *testing.T) { @@ -1188,7 +1211,7 @@ func Test_RegisterPeerByUser(t *testing.T) { metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) assert.NoError(t, err) - am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics) + am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock()) assert.NoError(t, err) existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" @@ -1252,7 +1275,7 @@ func Test_RegisterPeerBySetupKey(t *testing.T) { metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) assert.NoError(t, err) - am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics) + am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock()) assert.NoError(t, err) existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" @@ -1319,7 +1342,7 @@ func Test_RegisterPeerRollbackOnFailure(t *testing.T) { metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) assert.NoError(t, err) - am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics) + am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock()) assert.NoError(t, err) existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" diff --git a/management/server/peers/manager.go b/management/server/peers/manager.go new file mode 100644 index 000000000..1e067cef5 --- /dev/null +++ b/management/server/peers/manager.go @@ -0,0 +1,54 @@ +package peers + +import ( + "context" + "fmt" + + "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/permissions" + "github.com/netbirdio/netbird/management/server/status" + "github.com/netbirdio/netbird/management/server/store" +) + +type Manager interface { + GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error) + GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error) +} + +type managerImpl struct { + store store.Store + permissionsManager permissions.Manager +} + +func NewManager(store store.Store, permissionsManager permissions.Manager) Manager { + return &managerImpl{ + store: store, + permissionsManager: permissionsManager, + } +} + +func (m *managerImpl) GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error) { + allowed, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Peers, permissions.Read) + if err != nil { + return nil, fmt.Errorf("failed to validate user permissions: %w", err) + } + + if !allowed { + return nil, status.NewPermissionDeniedError() + } + + return m.store.GetPeerByID(ctx, store.LockingStrengthShare, accountID, peerID) +} + +func (m *managerImpl) GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error) { + allowed, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Peers, permissions.Read) + if err != nil { + return nil, fmt.Errorf("failed to validate user permissions: %w", err) + } + + if !allowed { + return nil, status.NewPermissionDeniedError() + } + + return m.store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "") +} diff --git a/management/server/policy.go b/management/server/policy.go index 45b3e93e6..d9f04f3a9 100644 --- a/management/server/policy.go +++ b/management/server/policy.go @@ -261,6 +261,7 @@ func toProtocolFirewallRules(rules []*types.FirewallRule) []*proto.FirewallRule Action: getProtoAction(rule.Action), Protocol: getProtoProtocol(rule.Protocol), Port: rule.Port, + PortInfo: rule.PortRange.ToProto(), } } return result diff --git a/management/server/policy_test.go b/management/server/policy_test.go index 73fc6edba..90f9670d1 100644 --- a/management/server/policy_test.go +++ b/management/server/policy_test.go @@ -284,7 +284,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) { for _, rule := range firewallRules { contains := false for _, expectedRule := range epectedFirewallRules { - if rule.IsEqual(expectedRule) { + if rule.Equal(expectedRule) { contains = true break } diff --git a/management/server/route_test.go b/management/server/route_test.go index 40e0f41b0..c5a5f2040 100644 --- a/management/server/route_test.go +++ b/management/server/route_test.go @@ -15,6 +15,7 @@ import ( "github.com/netbirdio/netbird/management/domain" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" networkTypes "github.com/netbirdio/netbird/management/server/networks/types" @@ -457,7 +458,7 @@ func TestCreateRoute(t *testing.T) { // assign generated ID testCase.expectedRoute.ID = outRoute.ID - if !testCase.expectedRoute.IsEqual(outRoute) { + if !testCase.expectedRoute.Equal(outRoute) { t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", outRoute, testCase.expectedRoute) } }) @@ -998,7 +999,7 @@ func TestSaveRoute(t *testing.T) { savedRoute, saved := account.Routes[testCase.expectedRoute.ID] require.True(t, saved) - if !testCase.expectedRoute.IsEqual(savedRoute) { + if !testCase.expectedRoute.Equal(savedRoute) { t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", savedRoute, testCase.expectedRoute) } }) @@ -1192,7 +1193,7 @@ func TestGetNetworkMap_RouteSync(t *testing.T) { peer1Routes, err := am.GetNetworkMap(context.Background(), peer1ID) require.NoError(t, err) require.Len(t, peer1Routes.Routes, 1, "we should receive one route for peer1") - require.True(t, expectedRoute.IsEqual(peer1Routes.Routes[0]), "received route should be equal") + require.True(t, expectedRoute.Equal(peer1Routes.Routes[0]), "received route should be equal") peer2Routes, err := am.GetNetworkMap(context.Background(), peer2ID) require.NoError(t, err) @@ -1204,7 +1205,7 @@ func TestGetNetworkMap_RouteSync(t *testing.T) { peer2Routes, err = am.GetNetworkMap(context.Background(), peer2ID) require.NoError(t, err) require.Len(t, peer2Routes.Routes, 1, "we should receive one route") - require.True(t, peer1Routes.Routes[0].IsEqual(peer2Routes.Routes[0]), "routes should be the same for peers in the same group") + require.True(t, peer1Routes.Routes[0].Equal(peer2Routes.Routes[0]), "routes should be the same for peers in the same group") newGroup := &types.Group{ ID: xid.New().String(), @@ -1256,7 +1257,7 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) { metrics, err := telemetry.NewDefaultAppMetrics(context.Background()) require.NoError(t, err) - return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics) + return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock()) } func createRouterStore(t *testing.T) (store.Store, error) { diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index efc2539ff..1dae3999b 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -1262,10 +1262,18 @@ func (s *SqlStore) GetPeerGroups(ctx context.Context, lockStrength LockingStreng } // GetAccountPeers retrieves peers for an account. -func (s *SqlStore) GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error) { +func (s *SqlStore) GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) { var peers []*nbpeer.Peer - result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Find(&peers, accountIDCondition, accountID) - if err := result.Error; err != nil { + query := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Where(accountIDCondition, accountID) + + if nameFilter != "" { + query = query.Where("name LIKE ?", "%"+nameFilter+"%") + } + if ipFilter != "" { + query = query.Where("ip LIKE ?", "%"+ipFilter+"%") + } + + if err := query.Find(&peers).Error; err != nil { log.WithContext(ctx).Errorf("failed to get peers from the store: %s", err) return nil, status.Errorf(status.Internal, "failed to get peers from store") } diff --git a/management/server/store/sql_store_test.go b/management/server/store/sql_store_test.go index 5cb092190..54649c5c1 100644 --- a/management/server/store/sql_store_test.go +++ b/management/server/store/sql_store_test.go @@ -2585,6 +2585,8 @@ func TestSqlStore_GetAccountPeers(t *testing.T) { tests := []struct { name string accountID string + nameFilter string + ipFilter string expectedCount int }{ { @@ -2602,11 +2604,29 @@ func TestSqlStore_GetAccountPeers(t *testing.T) { accountID: "", expectedCount: 0, }, + { + name: "should filter peers by name", + accountID: "bf1c8084-ba50-4ce7-9439-34653001fc3b", + nameFilter: "expiredhost", + expectedCount: 1, + }, + { + name: "should filter peers by partial name", + accountID: "bf1c8084-ba50-4ce7-9439-34653001fc3b", + nameFilter: "host", + expectedCount: 3, + }, + { + name: "should filter peers by ip", + accountID: "bf1c8084-ba50-4ce7-9439-34653001fc3b", + ipFilter: "100.64.39.54", + expectedCount: 1, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - peers, err := store.GetAccountPeers(context.Background(), LockingStrengthShare, tt.accountID) + peers, err := store.GetAccountPeers(context.Background(), LockingStrengthShare, tt.accountID, tt.nameFilter, tt.ipFilter) require.NoError(t, err) require.Len(t, peers, tt.expectedCount) }) diff --git a/management/server/store/store.go b/management/server/store/store.go index ece61d1f3..d84d699bb 100644 --- a/management/server/store/store.go +++ b/management/server/store/store.go @@ -120,7 +120,7 @@ type Store interface { RemoveResourceFromGroup(ctx context.Context, accountId string, groupID string, resourceID string) error AddPeerToAccount(ctx context.Context, lockStrength LockingStrength, peer *nbpeer.Peer) error GetPeerByPeerPubKey(ctx context.Context, lockStrength LockingStrength, peerKey string) (*nbpeer.Peer, error) - GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error) + GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) GetUserPeers(ctx context.Context, lockStrength LockingStrength, accountID, userID string) ([]*nbpeer.Peer, error) GetPeerByID(ctx context.Context, lockStrength LockingStrength, accountID string, peerID string) (*nbpeer.Peer, error) GetPeersByIDs(ctx context.Context, lockStrength LockingStrength, accountID string, peerIDs []string) (map[string]*nbpeer.Peer, error) diff --git a/management/server/types/firewall_rule.go b/management/server/types/firewall_rule.go index 4e405152c..10923828d 100644 --- a/management/server/types/firewall_rule.go +++ b/management/server/types/firewall_rule.go @@ -3,6 +3,7 @@ package types import ( "context" "fmt" + "reflect" "strconv" "strings" @@ -33,15 +34,14 @@ type FirewallRule struct { // Port of the traffic Port string + + // PortRange represents the range of ports for a firewall rule + PortRange RulePortRange } -// IsEqual checks if two firewall rules are equal. -func (r *FirewallRule) IsEqual(other *FirewallRule) bool { - return r.PeerIP == other.PeerIP && - r.Direction == other.Direction && - r.Action == other.Action && - r.Protocol == other.Protocol && - r.Port == other.Port +// Equal checks if two firewall rules are equal. +func (r *FirewallRule) Equal(other *FirewallRule) bool { + return reflect.DeepEqual(r, other) } // generateRouteFirewallRules generates a list of firewall rules for a given route. diff --git a/management/server/types/network.go b/management/server/types/network.go index d1fccd149..00082bb41 100644 --- a/management/server/types/network.go +++ b/management/server/types/network.go @@ -8,10 +8,13 @@ import ( "github.com/c-robinson/iplib" "github.com/rs/xid" + "golang.org/x/exp/maps" nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/proto" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/status" + "github.com/netbirdio/netbird/management/server/util" "github.com/netbirdio/netbird/route" ) @@ -33,6 +36,73 @@ type NetworkMap struct { OfflinePeers []*nbpeer.Peer FirewallRules []*FirewallRule RoutesFirewallRules []*RouteFirewallRule + ForwardingRules []*ForwardingRule +} + +func (nm *NetworkMap) Merge(other *NetworkMap) { + nm.Peers = mergeUniquePeersByID(nm.Peers, other.Peers) + nm.Routes = util.MergeUnique(nm.Routes, other.Routes) + nm.OfflinePeers = mergeUniquePeersByID(nm.OfflinePeers, other.OfflinePeers) + nm.FirewallRules = util.MergeUnique(nm.FirewallRules, other.FirewallRules) + nm.RoutesFirewallRules = util.MergeUnique(nm.RoutesFirewallRules, other.RoutesFirewallRules) + nm.ForwardingRules = util.MergeUnique(nm.ForwardingRules, other.ForwardingRules) +} + +func mergeUniquePeersByID(peers1, peers2 []*nbpeer.Peer) []*nbpeer.Peer { + result := make(map[string]*nbpeer.Peer) + for _, peer := range peers1 { + result[peer.ID] = peer + } + for _, peer := range peers2 { + if _, ok := result[peer.ID]; !ok { + result[peer.ID] = peer + } + } + + return maps.Values(result) +} + +type ForwardingRule struct { + RuleProtocol string + DestinationPorts RulePortRange + TranslatedAddress net.IP + TranslatedPorts RulePortRange +} + +func (f *ForwardingRule) ToProto() *proto.ForwardingRule { + var protocol proto.RuleProtocol + switch f.RuleProtocol { + case "icmp": + protocol = proto.RuleProtocol_ICMP + case "tcp": + protocol = proto.RuleProtocol_TCP + case "udp": + protocol = proto.RuleProtocol_UDP + case "all": + protocol = proto.RuleProtocol_ALL + default: + protocol = proto.RuleProtocol_UNKNOWN + } + return &proto.ForwardingRule{ + Protocol: protocol, + DestinationPort: f.DestinationPorts.ToProto(), + TranslatedAddress: ipToBytes(f.TranslatedAddress), + TranslatedPort: f.TranslatedPorts.ToProto(), + } +} + +func (f *ForwardingRule) Equal(other *ForwardingRule) bool { + return f.RuleProtocol == other.RuleProtocol && + f.DestinationPorts.Equal(&other.DestinationPorts) && + f.TranslatedAddress.Equal(other.TranslatedAddress) && + f.TranslatedPorts.Equal(&other.TranslatedPorts) +} + +func ipToBytes(ip net.IP) []byte { + if ip4 := ip.To4(); ip4 != nil { + return ip4 + } + return ip.To16() } type Network struct { diff --git a/management/server/types/policyrule.go b/management/server/types/policyrule.go index 721621a4b..b86732415 100644 --- a/management/server/types/policyrule.go +++ b/management/server/types/policyrule.go @@ -1,5 +1,9 @@ package types +import ( + "github.com/netbirdio/netbird/management/proto" +) + // PolicyUpdateOperationType operation type type PolicyUpdateOperationType int @@ -18,6 +22,21 @@ type RulePortRange struct { End uint16 } +func (r *RulePortRange) ToProto() *proto.PortInfo { + return &proto.PortInfo{ + PortSelection: &proto.PortInfo_Range_{ + Range: &proto.PortInfo_Range{ + Start: uint32(r.Start), + End: uint32(r.End), + }, + }, + } +} + +func (r *RulePortRange) Equal(other *RulePortRange) bool { + return r.Start == other.Start && r.End == other.End +} + // PolicyRule is the metadata of the policy type PolicyRule struct { // ID of the policy rule diff --git a/management/server/types/route_firewall_rule.go b/management/server/types/route_firewall_rule.go index 64708d68a..18eda7eda 100644 --- a/management/server/types/route_firewall_rule.go +++ b/management/server/types/route_firewall_rule.go @@ -30,3 +30,28 @@ type RouteFirewallRule struct { // isDynamic indicates whether the rule is for DNS routing IsDynamic bool } + +func (r *RouteFirewallRule) Equal(other *RouteFirewallRule) bool { + if r.Action != other.Action { + return false + } + if r.Destination != other.Destination { + return false + } + if r.Protocol != other.Protocol { + return false + } + if r.Port != other.Port { + return false + } + if !r.PortRange.Equal(&other.PortRange) { + return false + } + if !r.Domains.Equal(other.Domains) { + return false + } + if r.IsDynamic != other.IsDynamic { + return false + } + return true +} diff --git a/management/server/util/util.go b/management/server/util/util.go index d85b55f02..617484274 100644 --- a/management/server/util/util.go +++ b/management/server/util/util.go @@ -19,3 +19,34 @@ func Difference(a, b []string) []string { func ToPtr[T any](value T) *T { return &value } + +type comparableObject[T any] interface { + Equal(other T) bool +} + +func MergeUnique[T comparableObject[T]](arr1, arr2 []T) []T { + var result []T + + for _, item := range arr1 { + if !contains(result, item) { + result = append(result, item) + } + } + + for _, item := range arr2 { + if !contains(result, item) { + result = append(result, item) + } + } + + return result +} + +func contains[T comparableObject[T]](slice []T, element T) bool { + for _, item := range slice { + if item.Equal(element) { + return true + } + } + return false +} diff --git a/management/server/util/util_test.go b/management/server/util/util_test.go new file mode 100644 index 000000000..5c928b369 --- /dev/null +++ b/management/server/util/util_test.go @@ -0,0 +1,41 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type testObject struct { + value int +} + +func (t testObject) Equal(other testObject) bool { + return t.value == other.value +} + +func Test_MergeUniqueArraysWithoutDuplicates(t *testing.T) { + arr1 := []testObject{{value: 1}, {value: 2}} + arr2 := []testObject{{value: 2}, {value: 3}} + result := MergeUnique(arr1, arr2) + assert.Len(t, result, 3) + assert.Contains(t, result, testObject{value: 1}) + assert.Contains(t, result, testObject{value: 2}) + assert.Contains(t, result, testObject{value: 3}) +} + +func Test_MergeUniqueHandlesEmptyArrays(t *testing.T) { + arr1 := []testObject{} + arr2 := []testObject{} + result := MergeUnique(arr1, arr2) + assert.Empty(t, result) +} + +func Test_MergeUniqueHandlesOneEmptyArray(t *testing.T) { + arr1 := []testObject{{value: 1}, {value: 2}} + arr2 := []testObject{} + result := MergeUnique(arr1, arr2) + assert.Len(t, result, 2) + assert.Contains(t, result, testObject{value: 1}) + assert.Contains(t, result, testObject{value: 2}) +} diff --git a/route/route.go b/route/route.go index ad2aaba89..f7bf3ea87 100644 --- a/route/route.go +++ b/route/route.go @@ -132,8 +132,8 @@ func (r *Route) Copy() *Route { return route } -// IsEqual compares one route with the other -func (r *Route) IsEqual(other *Route) bool { +// Equal compares one route with the other +func (r *Route) Equal(other *Route) bool { if r == nil && other == nil { return true } else if r == nil || other == nil { From 3482852bb6234a3572242d768328a5f95bcec6ca Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 9 Mar 2025 18:02:33 +0100 Subject: [PATCH 13/30] sync proto and sum --- go.mod | 2 +- go.sum | 8 ++++++-- management/proto/management.pb.go | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index dfd3fa0bd..213362670 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/miekg/dns v1.1.59 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/nadoo/ipset v0.5.0 - github.com/netbirdio/management-integrations/integrations v0.0.0-20250226165736-0ac3dc443266 + github.com/netbirdio/management-integrations/integrations v0.0.0-20250307123548-fdd328baf934 github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d github.com/okta/okta-sdk-golang/v2 v2.18.0 github.com/oschwald/maxminddb-golang v1.12.0 diff --git a/go.sum b/go.sum index 2794c7546..7642345aa 100644 --- a/go.sum +++ b/go.sum @@ -529,8 +529,8 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250307154727-58660ea9a141 h1:GZUkZd9ZMBGahNt+AbYYvZrSMpOnaBLjHiBbloOE7sc= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250307154727-58660ea9a141/go.mod h1:A5QUfEZb5J3tw8EUB9e3q7Bgd/JtC0WlFT1onf3HPCY= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250307123548-fdd328baf934 h1:/fb+a/xr1U2xD1Sl3nAP3AdrUORHSWbsMVOrpB2PdPk= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250307123548-fdd328baf934/go.mod h1:1ubblPEYqdsR4L2bPIbkVvqBs6XYxxfW1C9PcoX067A= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d h1:bRq5TKgC7Iq20pDiuC54yXaWnAVeS5PdGpSokFTlR28= @@ -699,6 +699,10 @@ github.com/testcontainers/testcontainers-go/modules/postgres v0.31.0 h1:isAwFS3K github.com/testcontainers/testcontainers-go/modules/postgres v0.31.0/go.mod h1:ZNYY8vumNCEG9YI59A9d6/YaMY49uwRhmeU563EzFGw= github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0= github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ= +github.com/ti-mo/conntrack v0.5.1 h1:opEwkFICnDbQc0BUXl73PHBK0h23jEIFVjXsqvF4GY0= +github.com/ti-mo/conntrack v0.5.1/go.mod h1:T6NCbkMdVU4qEIgwL0njA6lw/iCAbzchlnwm1Sa314o= +github.com/ti-mo/netfilter v0.5.2 h1:CTjOwFuNNeZ9QPdRXt1MZFLFUf84cKtiQutNauHWd40= +github.com/ti-mo/netfilter v0.5.2/go.mod h1:Btx3AtFiOVdHReTDmP9AE+hlkOcvIy403u7BXXbWZKo= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= diff --git a/management/proto/management.pb.go b/management/proto/management.pb.go index 63722ac51..e7b72548b 100644 --- a/management/proto/management.pb.go +++ b/management/proto/management.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v4.24.3 +// protoc v3.21.9 // source: management.proto package proto From 036cecbf4650c82f02366be859cdd6577f26e4e4 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 9 Mar 2025 18:47:05 +0100 Subject: [PATCH 14/30] update integrations and go mod --- go.mod | 2 +- go.sum | 2 ++ management/server/grpcserver.go | 4 ++-- management/server/token_mgr.go | 7 +++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 213362670..b677c9721 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/miekg/dns v1.1.59 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/nadoo/ipset v0.5.0 - github.com/netbirdio/management-integrations/integrations v0.0.0-20250307123548-fdd328baf934 + github.com/netbirdio/management-integrations/integrations v0.0.0-20250309172507-ee34714caafc github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d github.com/okta/okta-sdk-golang/v2 v2.18.0 github.com/oschwald/maxminddb-golang v1.12.0 diff --git a/go.sum b/go.sum index 7642345aa..3191e3d35 100644 --- a/go.sum +++ b/go.sum @@ -531,6 +531,8 @@ github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6R github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q= github.com/netbirdio/management-integrations/integrations v0.0.0-20250307123548-fdd328baf934 h1:/fb+a/xr1U2xD1Sl3nAP3AdrUORHSWbsMVOrpB2PdPk= github.com/netbirdio/management-integrations/integrations v0.0.0-20250307123548-fdd328baf934/go.mod h1:1ubblPEYqdsR4L2bPIbkVvqBs6XYxxfW1C9PcoX067A= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250309172507-ee34714caafc h1:ighDmYrYbNGj+XMh8F0Ro5yDZ89PaNXRcM9eXrwtLHU= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250309172507-ee34714caafc/go.mod h1:gxlShs2JgyGBdGt4ctGVZsShX2Tcv8YFYt9QKr3jNhU= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d h1:bRq5TKgC7Iq20pDiuC54yXaWnAVeS5PdGpSokFTlR28= diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 4e72bd96f..500c261da 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -12,7 +12,7 @@ import ( pb "github.com/golang/protobuf/proto" // nolint "github.com/golang/protobuf/ptypes/timestamp" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip" - "github.com/netbirdio/management-integrations/integrations" + integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" log "github.com/sirupsen/logrus" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc/codes" @@ -605,7 +605,7 @@ func toNetbirdConfig(config *Config, turnCredentials *Token, relayToken *Token, Relay: relayCfg, } - integrations.ExtendNetBirdConfig(nbConfig, extraSettings) + integrationsConfig.ExtendNetBirdConfig(nbConfig, extraSettings) return nbConfig } diff --git a/management/server/token_mgr.go b/management/server/token_mgr.go index 7217b543f..f225e1654 100644 --- a/management/server/token_mgr.go +++ b/management/server/token_mgr.go @@ -9,10 +9,9 @@ import ( "sync" "time" + integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" log "github.com/sirupsen/logrus" - "github.com/netbirdio/management-integrations/integrations" - "github.com/netbirdio/netbird/management/proto" auth "github.com/netbirdio/netbird/relay/auth/hmac" authv2 "github.com/netbirdio/netbird/relay/auth/hmac/v2" @@ -218,7 +217,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Cont } } - integrations.ExtendNetBirdConfig(update.NetbirdConfig, nil) + integrationsConfig.ExtendNetBirdConfig(update.NetbirdConfig, nil) log.WithContext(ctx).Debugf("sending new TURN credentials to peer %s", peerID) m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update}) @@ -242,7 +241,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewRelayTokens(ctx context.Context, pe }, } - integrations.ExtendNetBirdConfig(update.NetbirdConfig, nil) + integrationsConfig.ExtendNetBirdConfig(update.NetbirdConfig, nil) log.WithContext(ctx).Debugf("sending new relay credentials to peer %s", peerID) m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update}) From 16fffdb75b846b47eefc893edf44472b14afc133 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 9 Mar 2025 18:48:48 +0100 Subject: [PATCH 15/30] sync changes from #3426 --- client/firewall/uspfilter/allow_netbird.go | 8 ++------ client/firewall/uspfilter/uspfilter_test.go | 12 ++++++------ client/internal/engine.go | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/client/firewall/uspfilter/allow_netbird.go b/client/firewall/uspfilter/allow_netbird.go index 4808c3090..5fe698aa9 100644 --- a/client/firewall/uspfilter/allow_netbird.go +++ b/client/firewall/uspfilter/allow_netbird.go @@ -9,12 +9,11 @@ import ( log "github.com/sirupsen/logrus" - "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack" "github.com/netbirdio/netbird/client/internal/statemanager" ) // Reset firewall to the default state -func (m *Manager) Reset(stateManager *statemanager.Manager) error { +func (m *Manager) Close(stateManager *statemanager.Manager) error { m.mutex.Lock() defer m.mutex.Unlock() @@ -23,17 +22,14 @@ func (m *Manager) Reset(stateManager *statemanager.Manager) error { if m.udpTracker != nil { m.udpTracker.Close() - m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout, m.logger, m.flowLogger) } if m.icmpTracker != nil { m.icmpTracker.Close() - m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout, m.logger, m.flowLogger) } if m.tcpTracker != nil { m.tcpTracker.Close() - m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout, m.logger, m.flowLogger) } if fwder := m.forwarder.Load(); fwder != nil { @@ -49,7 +45,7 @@ func (m *Manager) Reset(stateManager *statemanager.Manager) error { } if m.nativeFirewall != nil { - return m.nativeFirewall.Reset(stateManager) + return m.nativeFirewall.Close(stateManager) } return nil } diff --git a/client/firewall/uspfilter/uspfilter_test.go b/client/firewall/uspfilter/uspfilter_test.go index 1db572618..bc1ce4398 100644 --- a/client/firewall/uspfilter/uspfilter_test.go +++ b/client/firewall/uspfilter/uspfilter_test.go @@ -255,7 +255,7 @@ func TestManagerReset(t *testing.T) { return } - err = m.Reset(nil) + err = m.Close(nil) if err != nil { t.Errorf("failed to reset Manager: %v", err) return @@ -333,7 +333,7 @@ func TestNotMatchByIP(t *testing.T) { return } - if err = m.Reset(nil); err != nil { + if err = m.Close(nil); err != nil { t.Errorf("failed to reset Manager: %v", err) return } @@ -352,7 +352,7 @@ func TestRemovePacketHook(t *testing.T) { t.Fatalf("Failed to create Manager: %s", err) } defer func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }() // Add a UDP packet hook @@ -403,7 +403,7 @@ func TestProcessOutgoingHooks(t *testing.T) { manager.udpTracker.Close() manager.udpTracker = conntrack.NewUDPTracker(100*time.Millisecond, logger, flowLogger) defer func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }() manager.decoders = sync.Pool{ @@ -484,7 +484,7 @@ func TestUSPFilterCreatePerformance(t *testing.T) { time.Sleep(time.Second) defer func() { - if err := manager.Reset(nil); err != nil { + if err := manager.Close(nil); err != nil { t.Errorf("clear the manager state: %v", err) } time.Sleep(time.Second) @@ -530,7 +530,7 @@ func TestStatefulFirewall_UDPTracking(t *testing.T) { }, } defer func() { - require.NoError(t, manager.Reset(nil)) + require.NoError(t, manager.Close(nil)) }() // Set up packet parameters diff --git a/client/internal/engine.go b/client/internal/engine.go index 9e5d8a42d..e54ff35f6 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -1428,7 +1428,7 @@ func (e *Engine) close() { } if e.firewall != nil { - err := e.firewall.Reset(e.stateManager) + err := e.firewall.Close(e.stateManager) if err != nil { log.Warnf("failed to reset firewall: %s", err) } From a2962b4ce023f40ec943ef6cf957d7419cf0cae1 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 9 Mar 2025 18:50:20 +0100 Subject: [PATCH 16/30] sync go.sum --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index 3191e3d35..5ee31539a 100644 --- a/go.sum +++ b/go.sum @@ -529,8 +529,6 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250307123548-fdd328baf934 h1:/fb+a/xr1U2xD1Sl3nAP3AdrUORHSWbsMVOrpB2PdPk= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250307123548-fdd328baf934/go.mod h1:1ubblPEYqdsR4L2bPIbkVvqBs6XYxxfW1C9PcoX067A= github.com/netbirdio/management-integrations/integrations v0.0.0-20250309172507-ee34714caafc h1:ighDmYrYbNGj+XMh8F0Ro5yDZ89PaNXRcM9eXrwtLHU= github.com/netbirdio/management-integrations/integrations v0.0.0-20250309172507-ee34714caafc/go.mod h1:gxlShs2JgyGBdGt4ctGVZsShX2Tcv8YFYt9QKr3jNhU= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8= From 8430139d807c18dee187a44f616b217188d9285b Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 9 Mar 2025 19:03:57 +0100 Subject: [PATCH 17/30] fix missing method --- client/firewall/uspfilter/allow_netbird_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/firewall/uspfilter/allow_netbird_windows.go b/client/firewall/uspfilter/allow_netbird_windows.go index ff80fec41..f63792fec 100644 --- a/client/firewall/uspfilter/allow_netbird_windows.go +++ b/client/firewall/uspfilter/allow_netbird_windows.go @@ -23,7 +23,7 @@ const ( ) // Reset firewall to the default state -func (m *Manager) Reset(*statemanager.Manager) error { +func (m *Manager) Close(*statemanager.Manager) error { m.mutex.Lock() defer m.mutex.Unlock() From d8bcf745b00ce59b6a1bf794e42935f13e40b2f8 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 9 Mar 2025 19:32:38 +0100 Subject: [PATCH 18/30] update integrations --- go.mod | 2 +- go.sum | 4 ++-- management/server/grpcserver.go | 4 ++-- management/server/token_mgr.go | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index b677c9721..b0b697877 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/miekg/dns v1.1.59 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/nadoo/ipset v0.5.0 - github.com/netbirdio/management-integrations/integrations v0.0.0-20250309172507-ee34714caafc + github.com/netbirdio/management-integrations/integrations v0.0.0-20250309183036-fe518506f9de github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d github.com/okta/okta-sdk-golang/v2 v2.18.0 github.com/oschwald/maxminddb-golang v1.12.0 diff --git a/go.sum b/go.sum index 5ee31539a..20c49ed13 100644 --- a/go.sum +++ b/go.sum @@ -529,8 +529,8 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250309172507-ee34714caafc h1:ighDmYrYbNGj+XMh8F0Ro5yDZ89PaNXRcM9eXrwtLHU= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250309172507-ee34714caafc/go.mod h1:gxlShs2JgyGBdGt4ctGVZsShX2Tcv8YFYt9QKr3jNhU= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250309183036-fe518506f9de h1:YK4gywy+JKZXu2x/jQPaxvLFs6b5nR97yP9mtjmWru8= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250309183036-fe518506f9de/go.mod h1:gxlShs2JgyGBdGt4ctGVZsShX2Tcv8YFYt9QKr3jNhU= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d h1:bRq5TKgC7Iq20pDiuC54yXaWnAVeS5PdGpSokFTlR28= diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 500c261da..4e72bd96f 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -12,7 +12,7 @@ import ( pb "github.com/golang/protobuf/proto" // nolint "github.com/golang/protobuf/ptypes/timestamp" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip" - integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" + "github.com/netbirdio/management-integrations/integrations" log "github.com/sirupsen/logrus" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc/codes" @@ -605,7 +605,7 @@ func toNetbirdConfig(config *Config, turnCredentials *Token, relayToken *Token, Relay: relayCfg, } - integrationsConfig.ExtendNetBirdConfig(nbConfig, extraSettings) + integrations.ExtendNetBirdConfig(nbConfig, extraSettings) return nbConfig } diff --git a/management/server/token_mgr.go b/management/server/token_mgr.go index f225e1654..d3e7a3253 100644 --- a/management/server/token_mgr.go +++ b/management/server/token_mgr.go @@ -9,7 +9,7 @@ import ( "sync" "time" - integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" + "github.com/netbirdio/management-integrations/integrations" log "github.com/sirupsen/logrus" "github.com/netbirdio/netbird/management/proto" @@ -217,7 +217,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Cont } } - integrationsConfig.ExtendNetBirdConfig(update.NetbirdConfig, nil) + integrations.ExtendNetBirdConfig(update.NetbirdConfig, nil) log.WithContext(ctx).Debugf("sending new TURN credentials to peer %s", peerID) m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update}) @@ -241,7 +241,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewRelayTokens(ctx context.Context, pe }, } - integrationsConfig.ExtendNetBirdConfig(update.NetbirdConfig, nil) + integrations.ExtendNetBirdConfig(update.NetbirdConfig, nil) log.WithContext(ctx).Debugf("sending new relay credentials to peer %s", peerID) m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update}) From c73481aee45abe5bb8f2963956f15cc358c372ad Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:30:49 +0100 Subject: [PATCH 19/30] [client] Enable windows stderr logs by default (#3476) --- client/server/panic_windows.go | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/client/server/panic_windows.go b/client/server/panic_windows.go index 1d4ba4b75..c5e73be7c 100644 --- a/client/server/panic_windows.go +++ b/client/server/panic_windows.go @@ -3,7 +3,7 @@ package server import ( "fmt" "os" - "path/filepath" + "path" "syscall" log "github.com/sirupsen/logrus" @@ -12,7 +12,6 @@ import ( ) const ( - windowsPanicLogEnvVar = "NB_WINDOWS_PANIC_LOG" // STD_ERROR_HANDLE ((DWORD)-12) = 4294967284 stdErrorHandle = ^uintptr(11) ) @@ -25,13 +24,10 @@ var ( ) func handlePanicLog() error { - logPath := os.Getenv(windowsPanicLogEnvVar) - if logPath == "" { - return nil - } + // TODO: move this to a central location + logDir := path.Join(os.Getenv("PROGRAMDATA"), "Netbird") + logPath := path.Join(logDir, "netbird.err") - // Ensure the directory exists - logDir := filepath.Dir(logPath) if err := os.MkdirAll(logDir, 0750); err != nil { return fmt.Errorf("create panic log directory: %w", err) } @@ -39,13 +35,11 @@ func handlePanicLog() error { return fmt.Errorf("enforce permission on panic log file: %w", err) } - // Open log file with append mode f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { return fmt.Errorf("open panic log file: %w", err) } - // Redirect stderr to the file if err = redirectStderr(f); err != nil { if closeErr := f.Close(); closeErr != nil { log.Warnf("failed to close file after redirect error: %v", closeErr) @@ -59,7 +53,6 @@ func handlePanicLog() error { // redirectStderr redirects stderr to the provided file func redirectStderr(f *os.File) error { - // Get the current process's stderr handle if err := setStdHandle(f); err != nil { return fmt.Errorf("failed to set stderr handle: %w", err) } From 81040ff80a40c6fac95520468b17c649453be0ae Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Mon, 10 Mar 2025 11:52:36 +0100 Subject: [PATCH 20/30] [docs] Update typo (#3477) --- .../observability/grafana/dashboards/signal.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infrastructure_files/observability/grafana/dashboards/signal.json b/infrastructure_files/observability/grafana/dashboards/signal.json index 5e36f6ce6..0dc1b7aa6 100644 --- a/infrastructure_files/observability/grafana/dashboards/signal.json +++ b/infrastructure_files/observability/grafana/dashboards/signal.json @@ -757,7 +757,7 @@ }, "id": 18, "panels": [], - "title": "Core metrics / registerations", + "title": "Core metrics / registrations", "type": "row" }, { @@ -1874,4 +1874,4 @@ "uid": "cebyq0fs0m-v001", "version": 15, "weekStart": "" - } \ No newline at end of file + } From 6bef474e9e8aedce9a18698847887535e01d77d5 Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Mon, 10 Mar 2025 13:16:28 +0100 Subject: [PATCH 21/30] [client] Prevent panic in case of double close call (#3475) Prevent panic in case of double close call --- client/firewall/uspfilter/conntrack/icmp.go | 18 +++++++++++------- client/firewall/uspfilter/conntrack/tcp.go | 19 ++++++++++++------- client/firewall/uspfilter/conntrack/udp.go | 18 +++++++++++------- .../firewall/uspfilter/conntrack/udp_test.go | 10 +++++++--- 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/client/firewall/uspfilter/conntrack/icmp.go b/client/firewall/uspfilter/conntrack/icmp.go index 25cd9e87d..a8cb01565 100644 --- a/client/firewall/uspfilter/conntrack/icmp.go +++ b/client/firewall/uspfilter/conntrack/icmp.go @@ -1,6 +1,7 @@ package conntrack import ( + "context" "net" "sync" "time" @@ -39,8 +40,8 @@ type ICMPTracker struct { connections map[ICMPConnKey]*ICMPConnTrack timeout time.Duration cleanupTicker *time.Ticker + tickerCancel context.CancelFunc mutex sync.RWMutex - done chan struct{} ipPool *PreallocatedIPs } @@ -50,16 +51,18 @@ func NewICMPTracker(timeout time.Duration, logger *nblog.Logger) *ICMPTracker { timeout = DefaultICMPTimeout } + ctx, cancel := context.WithCancel(context.Background()) + tracker := &ICMPTracker{ logger: logger, connections: make(map[ICMPConnKey]*ICMPConnTrack), timeout: timeout, cleanupTicker: time.NewTicker(ICMPCleanupInterval), - done: make(chan struct{}), + tickerCancel: cancel, ipPool: NewPreallocatedIPs(), } - go tracker.cleanupRoutine() + go tracker.cleanupRoutine(ctx) return tracker } @@ -119,12 +122,14 @@ func (t *ICMPTracker) IsValidInbound(srcIP net.IP, dstIP net.IP, id uint16, seq conn.Sequence == seq } -func (t *ICMPTracker) cleanupRoutine() { +func (t *ICMPTracker) cleanupRoutine(ctx context.Context) { + defer t.tickerCancel() + for { select { case <-t.cleanupTicker.C: t.cleanup() - case <-t.done: + case <-ctx.Done(): return } } @@ -146,8 +151,7 @@ func (t *ICMPTracker) cleanup() { // Close stops the cleanup routine and releases resources func (t *ICMPTracker) Close() { - t.cleanupTicker.Stop() - close(t.done) + t.tickerCancel() t.mutex.Lock() for _, conn := range t.connections { diff --git a/client/firewall/uspfilter/conntrack/tcp.go b/client/firewall/uspfilter/conntrack/tcp.go index 7c12e8ad0..1b5cbae95 100644 --- a/client/firewall/uspfilter/conntrack/tcp.go +++ b/client/firewall/uspfilter/conntrack/tcp.go @@ -3,6 +3,7 @@ package conntrack // TODO: Send RST packets for invalid/timed-out connections import ( + "context" "net" "sync" "sync/atomic" @@ -85,23 +86,26 @@ type TCPTracker struct { connections map[ConnKey]*TCPConnTrack mutex sync.RWMutex cleanupTicker *time.Ticker - done chan struct{} + tickerCancel context.CancelFunc timeout time.Duration ipPool *PreallocatedIPs } // NewTCPTracker creates a new TCP connection tracker func NewTCPTracker(timeout time.Duration, logger *nblog.Logger) *TCPTracker { + + ctx, cancel := context.WithCancel(context.Background()) + tracker := &TCPTracker{ logger: logger, connections: make(map[ConnKey]*TCPConnTrack), cleanupTicker: time.NewTicker(TCPCleanupInterval), - done: make(chan struct{}), + tickerCancel: cancel, timeout: timeout, ipPool: NewPreallocatedIPs(), } - go tracker.cleanupRoutine() + go tracker.cleanupRoutine(ctx) return tracker } @@ -315,12 +319,14 @@ func (t *TCPTracker) isValidStateForFlags(state TCPState, flags uint8) bool { return false } -func (t *TCPTracker) cleanupRoutine() { +func (t *TCPTracker) cleanupRoutine(ctx context.Context) { + defer t.cleanupTicker.Stop() + for { select { case <-t.cleanupTicker.C: t.cleanup() - case <-t.done: + case <-ctx.Done(): return } } @@ -355,8 +361,7 @@ func (t *TCPTracker) cleanup() { // Close stops the cleanup routine and releases resources func (t *TCPTracker) Close() { - t.cleanupTicker.Stop() - close(t.done) + t.tickerCancel() // Clean up all remaining IPs t.mutex.Lock() diff --git a/client/firewall/uspfilter/conntrack/udp.go b/client/firewall/uspfilter/conntrack/udp.go index e73465e31..073eb0fa2 100644 --- a/client/firewall/uspfilter/conntrack/udp.go +++ b/client/firewall/uspfilter/conntrack/udp.go @@ -1,6 +1,7 @@ package conntrack import ( + "context" "net" "sync" "time" @@ -26,8 +27,8 @@ type UDPTracker struct { connections map[ConnKey]*UDPConnTrack timeout time.Duration cleanupTicker *time.Ticker + tickerCancel context.CancelFunc mutex sync.RWMutex - done chan struct{} ipPool *PreallocatedIPs } @@ -37,16 +38,18 @@ func NewUDPTracker(timeout time.Duration, logger *nblog.Logger) *UDPTracker { timeout = DefaultUDPTimeout } + ctx, cancel := context.WithCancel(context.Background()) + tracker := &UDPTracker{ logger: logger, connections: make(map[ConnKey]*UDPConnTrack), timeout: timeout, cleanupTicker: time.NewTicker(UDPCleanupInterval), - done: make(chan struct{}), + tickerCancel: cancel, ipPool: NewPreallocatedIPs(), } - go tracker.cleanupRoutine() + go tracker.cleanupRoutine(ctx) return tracker } @@ -103,12 +106,14 @@ func (t *UDPTracker) IsValidInbound(srcIP net.IP, dstIP net.IP, srcPort uint16, } // cleanupRoutine periodically removes stale connections -func (t *UDPTracker) cleanupRoutine() { +func (t *UDPTracker) cleanupRoutine(ctx context.Context) { + defer t.cleanupTicker.Stop() + for { select { case <-t.cleanupTicker.C: t.cleanup() - case <-t.done: + case <-ctx.Done(): return } } @@ -131,8 +136,7 @@ func (t *UDPTracker) cleanup() { // Close stops the cleanup routine and releases resources func (t *UDPTracker) Close() { - t.cleanupTicker.Stop() - close(t.done) + t.tickerCancel() t.mutex.Lock() for _, conn := range t.connections { diff --git a/client/firewall/uspfilter/conntrack/udp_test.go b/client/firewall/uspfilter/conntrack/udp_test.go index fa83ee356..40e73cbe0 100644 --- a/client/firewall/uspfilter/conntrack/udp_test.go +++ b/client/firewall/uspfilter/conntrack/udp_test.go @@ -1,6 +1,7 @@ package conntrack import ( + "context" "net" "testing" "time" @@ -34,7 +35,7 @@ func TestNewUDPTracker(t *testing.T) { assert.Equal(t, tt.wantTimeout, tracker.timeout) assert.NotNil(t, tracker.connections) assert.NotNil(t, tracker.cleanupTicker) - assert.NotNil(t, tracker.done) + assert.NotNil(t, tracker.tickerCancel) }) } } @@ -154,18 +155,21 @@ func TestUDPTracker_Cleanup(t *testing.T) { timeout := 50 * time.Millisecond cleanupInterval := 25 * time.Millisecond + ctx, tickerCancel := context.WithCancel(context.Background()) + defer tickerCancel() + // Create tracker with custom cleanup interval tracker := &UDPTracker{ connections: make(map[ConnKey]*UDPConnTrack), timeout: timeout, cleanupTicker: time.NewTicker(cleanupInterval), - done: make(chan struct{}), + tickerCancel: tickerCancel, ipPool: NewPreallocatedIPs(), logger: logger, } // Start cleanup routine - go tracker.cleanupRoutine() + go tracker.cleanupRoutine(ctx) // Add some connections connections := []struct { From aaa23beeecc461d7ac92838ffca91d56989f81dc Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Mon, 10 Mar 2025 13:17:09 +0100 Subject: [PATCH 22/30] [client] Prevent to block channel writing (#3474) The "runningChan" provides feedback to the UI or any client about whether the service is up and running. If the client exits earlier than when the service successfully starts, then this channel causes a block. - Added timeout for reading the channel to ensure we don't cause blocks for too long for the caller - Modified channel writing operations to be non-blocking --- client/embed/embed.go | 15 ++++++--------- client/internal/connect.go | 14 +++++++------- client/server/server.go | 19 ++++++++++--------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/client/embed/embed.go b/client/embed/embed.go index 9ded618c5..fe95b1942 100644 --- a/client/embed/embed.go +++ b/client/embed/embed.go @@ -134,10 +134,11 @@ func (c *Client) Start(startCtx context.Context) error { // either startup error (permanent backoff err) or nil err (successful engine up) // TODO: make after-startup backoff err available - run := make(chan error, 1) + run := make(chan struct{}, 1) + clientErr := make(chan error, 1) go func() { if err := client.Run(run); err != nil { - run <- err + clientErr <- err } }() @@ -147,13 +148,9 @@ func (c *Client) Start(startCtx context.Context) error { return fmt.Errorf("stop error after context done. Stop error: %w. Context done: %w", stopErr, startCtx.Err()) } return startCtx.Err() - case err := <-run: - if err != nil { - if stopErr := client.Stop(); stopErr != nil { - return fmt.Errorf("stop error after failed to startup. Stop error: %w. Start error: %w", stopErr, err) - } - return fmt.Errorf("startup: %w", err) - } + case err := <-clientErr: + return fmt.Errorf("startup: %w", err) + case <-run: } c.connect = client diff --git a/client/internal/connect.go b/client/internal/connect.go index bf513ed39..7cbe47b74 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -61,7 +61,7 @@ func NewConnectClient( } // Run with main logic. -func (c *ConnectClient) Run(runningChan chan error) error { +func (c *ConnectClient) Run(runningChan chan struct{}) error { return c.run(MobileDependency{}, runningChan) } @@ -102,7 +102,7 @@ func (c *ConnectClient) RunOniOS( return c.run(mobileDependency, nil) } -func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan error) error { +func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan struct{}) error { defer func() { if r := recover(); r != nil { rec := c.statusRecorder @@ -159,7 +159,6 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan } defer c.statusRecorder.ClientStop() - runningChanOpen := true operation := func() error { // if context cancelled we not start new backoff cycle if c.isContextCancelled() { @@ -282,10 +281,11 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan log.Infof("Netbird engine started, the IP is: %s", peerConfig.GetAddress()) state.Set(StatusConnected) - if runningChan != nil && runningChanOpen { - runningChan <- nil - close(runningChan) - runningChanOpen = false + if runningChan != nil { + select { + case runningChan <- struct{}{}: + default: + } } <-engineCtx.Done() diff --git a/client/server/server.go b/client/server/server.go index 8907f541f..b112a994f 100644 --- a/client/server/server.go +++ b/client/server/server.go @@ -160,7 +160,7 @@ func (s *Server) Start() error { // mechanism to keep the client connected even when the connection is lost. // we cancel retry if the client receive a stop or down command, or if disable auto connect is configured. func (s *Server) connectWithRetryRuns(ctx context.Context, config *internal.Config, statusRecorder *peer.Status, - runningChan chan error, + runningChan chan struct{}, ) { backOff := getConnectWithBackoff(ctx) retryStarted := false @@ -628,20 +628,21 @@ func (s *Server) Up(callerCtx context.Context, _ *proto.UpRequest) (*proto.UpRes s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String()) s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive) - runningChan := make(chan error) - go s.connectWithRetryRuns(ctx, s.config, s.statusRecorder, runningChan) + timeoutCtx, cancel := context.WithTimeout(callerCtx, 10*time.Second) + defer cancel() + runningChan := make(chan struct{}, 1) // buffered channel to do not lose the signal + go s.connectWithRetryRuns(ctx, s.config, s.statusRecorder, runningChan) for { select { - case err := <-runningChan: - if err != nil { - log.Debugf("waiting for engine to become ready failed: %s", err) - } else { - return &proto.UpResponse{}, nil - } + case <-runningChan: + return &proto.UpResponse{}, nil case <-callerCtx.Done(): log.Debug("context done, stopping the wait for engine to become ready") return nil, callerCtx.Err() + case <-timeoutCtx.Done(): + log.Debug("up is timed out, stopping the wait for engine to become ready") + return nil, timeoutCtx.Err() } } } From e66e329bf60c1d8b67fed2b84adb754e9f720e85 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Mon, 10 Mar 2025 13:19:17 +0100 Subject: [PATCH 23/30] [client] Add option to autostart netbird ui in the Windows installer (#3469) --- client/installer.nsis | 48 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/client/installer.nsis b/client/installer.nsis index 743c81a6d..5219058a8 100644 --- a/client/installer.nsis +++ b/client/installer.nsis @@ -22,6 +22,8 @@ !define UI_REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${UI_APP_EXE}" !define UI_UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UI_APP_NAME}" +!define AUTOSTART_REG_KEY "Software\Microsoft\Windows\CurrentVersion\Run" + Unicode True ###################################################################### @@ -68,6 +70,9 @@ ShowInstDetails Show !insertmacro MUI_PAGE_DIRECTORY +; Custom page for autostart checkbox +Page custom AutostartPage AutostartPageLeave + !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH @@ -80,8 +85,36 @@ ShowInstDetails Show !insertmacro MUI_LANGUAGE "English" +; Variables for autostart option +Var AutostartCheckbox +Var AutostartEnabled + ###################################################################### +; Function to create the autostart options page +Function AutostartPage + !insertmacro MUI_HEADER_TEXT "Startup Options" "Configure how ${APP_NAME} launches with Windows." + + nsDialogs::Create 1018 + Pop $0 + + ${If} $0 == error + Abort + ${EndIf} + + ${NSD_CreateCheckbox} 0 20u 100% 10u "Start ${APP_NAME} UI automatically when Windows starts" + Pop $AutostartCheckbox + ${NSD_Check} $AutostartCheckbox ; Default to checked + StrCpy $AutostartEnabled "1" ; Default to enabled + + nsDialogs::Show +FunctionEnd + +; Function to handle leaving the autostart page +Function AutostartPageLeave + ${NSD_GetState} $AutostartCheckbox $AutostartEnabled +FunctionEnd + Function GetAppFromCommand Exch $1 Push $2 @@ -163,6 +196,16 @@ WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}" WriteRegStr ${REG_ROOT} "${UI_REG_APP_PATH}" "" "$INSTDIR\${UI_APP_EXE}" +; Create autostart registry entry based on checkbox +DetailPrint "Autostart enabled: $AutostartEnabled" +${If} $AutostartEnabled == "1" + WriteRegStr HKCU "${AUTOSTART_REG_KEY}" "${APP_NAME}" "$INSTDIR\${UI_APP_EXE}.exe" + DetailPrint "Added autostart registry entry: $INSTDIR\${UI_APP_EXE}.exe" +${Else} + DeleteRegValue HKCU "${AUTOSTART_REG_KEY}" "${APP_NAME}" + DetailPrint "Autostart not enabled by user" +${EndIf} + EnVar::SetHKLM EnVar::AddValueEx "path" "$INSTDIR" @@ -186,7 +229,10 @@ ExecWait '"$INSTDIR\${MAIN_APP_EXE}" service stop' ExecWait '"$INSTDIR\${MAIN_APP_EXE}" service uninstall' # kill ui client -ExecWait `taskkill /im ${UI_APP_EXE}.exe` +ExecWait `taskkill /im ${UI_APP_EXE}.exe /f` + +; Remove autostart registry entry +DeleteRegValue HKCU "${AUTOSTART_REG_KEY}" "${APP_NAME}" # wait the service uninstall take unblock the executable Sleep 3000 From 636a0e2475ac28e68ff0b8899a64483f1be5fed4 Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Mon, 10 Mar 2025 13:32:12 +0100 Subject: [PATCH 24/30] [client] Fix engine restart (#3435) - Refactor the network monitoring to handle one event and it after return - In the engine restart cancel the upper layer context and the responsibility of the engine stop will be the upper layer - Before triggering a restart, the engine checks whether the state is already down. This helps avoid unnecessary delayed network restart events. --- client/internal/connect.go | 11 +- client/internal/engine.go | 48 +++----- .../{monitor_bsd.go => check_change_bsd.go} | 16 +-- ...monitor_linux.go => check_change_linux.go} | 7 +- ...tor_windows.go => check_change_windows.go} | 13 +- client/internal/networkmonitor/monitor.go | 113 +++++++++++++++++- .../networkmonitor/monitor_generic.go | 82 ------------- .../internal/networkmonitor/monitor_mobile.go | 17 ++- .../internal/networkmonitor/monitor_test.go | 99 +++++++++++++++ 9 files changed, 254 insertions(+), 152 deletions(-) rename client/internal/networkmonitor/{monitor_bsd.go => check_change_bsd.go} (90%) rename client/internal/networkmonitor/{monitor_linux.go => check_change_linux.go} (93%) rename client/internal/networkmonitor/{monitor_windows.go => check_change_windows.go} (89%) delete mode 100644 client/internal/networkmonitor/monitor_generic.go create mode 100644 client/internal/networkmonitor/monitor_test.go diff --git a/client/internal/connect.go b/client/internal/connect.go index 7cbe47b74..504c88c6f 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -161,7 +161,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan defer c.statusRecorder.ClientStop() operation := func() error { // if context cancelled we not start new backoff cycle - if c.isContextCancelled() { + if c.ctx.Err() != nil { return nil } @@ -379,15 +379,6 @@ func (c *ConnectClient) Stop() error { return nil } -func (c *ConnectClient) isContextCancelled() bool { - select { - case <-c.ctx.Done(): - return true - default: - return false - } -} - // SetNetworkMapPersistence enables or disables network map persistence. // When enabled, the last received network map will be stored and can be retrieved // through the Engine's getLatestNetworkMap method. When disabled, any stored diff --git a/client/internal/engine.go b/client/internal/engine.go index cedf8364c..2693976dd 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -1589,16 +1589,19 @@ func (e *Engine) probeTURNs() []relay.ProbeResult { return relay.ProbeAll(e.ctx, relay.ProbeTURN, turns) } +// restartEngine restarts the engine by cancelling the client context func (e *Engine) restartEngine() { - log.Info("restarting engine") - CtxGetState(e.ctx).Set(StatusConnecting) + e.syncMsgMux.Lock() + defer e.syncMsgMux.Unlock() - if err := e.Stop(); err != nil { - log.Errorf("Failed to stop engine: %v", err) + if e.ctx.Err() != nil { + return } + log.Info("restarting engine") + CtxGetState(e.ctx).Set(StatusConnecting) _ = CtxGetState(e.ctx).Wrap(ErrResetConnection) - log.Infof("cancelling client, engine will be recreated") + log.Infof("cancelling client context, engine will be recreated") e.clientCancel() } @@ -1610,34 +1613,17 @@ func (e *Engine) startNetworkMonitor() { e.networkMonitor = networkmonitor.New() go func() { - var mu sync.Mutex - var debounceTimer *time.Timer - - // Start the network monitor with a callback, Start will block until the monitor is stopped, - // a network change is detected, or an error occurs on start up - err := e.networkMonitor.Start(e.ctx, func() { - // This function is called when a network change is detected - mu.Lock() - defer mu.Unlock() - - if debounceTimer != nil { - log.Infof("Network monitor: detected network change, reset debounceTimer") - debounceTimer.Stop() + if err := e.networkMonitor.Listen(e.ctx); err != nil { + if errors.Is(err, context.Canceled) { + log.Infof("network monitor stopped") + return } - - // Set a new timer to debounce rapid network changes - debounceTimer = time.AfterFunc(2*time.Second, func() { - // This function is called after the debounce period - mu.Lock() - defer mu.Unlock() - - log.Infof("Network monitor: detected network change, restarting engine") - e.restartEngine() - }) - }) - if err != nil && !errors.Is(err, networkmonitor.ErrStopped) { - log.Errorf("Network monitor: %v", err) + log.Errorf("network monitor error: %v", err) + return } + + log.Infof("Network monitor: detected network change, restarting engine") + e.restartEngine() }() } diff --git a/client/internal/networkmonitor/monitor_bsd.go b/client/internal/networkmonitor/check_change_bsd.go similarity index 90% rename from client/internal/networkmonitor/monitor_bsd.go rename to client/internal/networkmonitor/check_change_bsd.go index 4dc2c1aa3..bb327a877 100644 --- a/client/internal/networkmonitor/monitor_bsd.go +++ b/client/internal/networkmonitor/check_change_bsd.go @@ -16,7 +16,7 @@ import ( "github.com/netbirdio/netbird/client/internal/routemanager/systemops" ) -func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, callback func()) error { +func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error { fd, err := unix.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) if err != nil { return fmt.Errorf("failed to open routing socket: %v", err) @@ -28,18 +28,10 @@ func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, ca } }() - go func() { - <-ctx.Done() - err := unix.Close(fd) - if err != nil && !errors.Is(err, unix.EBADF) { - log.Debugf("Network monitor: closed routing socket: %v", err) - } - }() - for { select { case <-ctx.Done(): - return ErrStopped + return ctx.Err() default: buf := make([]byte, 2048) n, err := unix.Read(fd, buf) @@ -76,11 +68,11 @@ func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, ca switch msg.Type { case unix.RTM_ADD: log.Infof("Network monitor: default route changed: via %s, interface %s", route.Gw, intf) - go callback() + return nil case unix.RTM_DELETE: if nexthopv4.Intf != nil && route.Gw.Compare(nexthopv4.IP) == 0 || nexthopv6.Intf != nil && route.Gw.Compare(nexthopv6.IP) == 0 { log.Infof("Network monitor: default route removed: via %s, interface %s", route.Gw, intf) - go callback() + return nil } } } diff --git a/client/internal/networkmonitor/monitor_linux.go b/client/internal/networkmonitor/check_change_linux.go similarity index 93% rename from client/internal/networkmonitor/monitor_linux.go rename to client/internal/networkmonitor/check_change_linux.go index 035be1f09..efd8b5884 100644 --- a/client/internal/networkmonitor/monitor_linux.go +++ b/client/internal/networkmonitor/check_change_linux.go @@ -14,7 +14,7 @@ import ( "github.com/netbirdio/netbird/client/internal/routemanager/systemops" ) -func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, callback func()) error { +func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error { if nexthopv4.Intf == nil && nexthopv6.Intf == nil { return errors.New("no interfaces available") } @@ -31,8 +31,7 @@ func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, ca for { select { case <-ctx.Done(): - return ErrStopped - + return ctx.Err() // handle route changes case route := <-routeChan: // default route and main table @@ -43,12 +42,10 @@ func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, ca // triggered on added/replaced routes case syscall.RTM_NEWROUTE: log.Infof("Network monitor: default route changed: via %s, interface %d", route.Gw, route.LinkIndex) - go callback() return nil case syscall.RTM_DELROUTE: if nexthopv4.Intf != nil && route.Gw.Equal(nexthopv4.IP.AsSlice()) || nexthopv6.Intf != nil && route.Gw.Equal(nexthopv6.IP.AsSlice()) { log.Infof("Network monitor: default route removed: via %s, interface %d", route.Gw, route.LinkIndex) - go callback() return nil } } diff --git a/client/internal/networkmonitor/monitor_windows.go b/client/internal/networkmonitor/check_change_windows.go similarity index 89% rename from client/internal/networkmonitor/monitor_windows.go rename to client/internal/networkmonitor/check_change_windows.go index cd48c269d..582865738 100644 --- a/client/internal/networkmonitor/monitor_windows.go +++ b/client/internal/networkmonitor/check_change_windows.go @@ -10,7 +10,7 @@ import ( "github.com/netbirdio/netbird/client/internal/routemanager/systemops" ) -func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, callback func()) error { +func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error { routeMonitor, err := systemops.NewRouteMonitor(ctx) if err != nil { return fmt.Errorf("failed to create route monitor: %w", err) @@ -24,20 +24,20 @@ func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, ca for { select { case <-ctx.Done(): - return ErrStopped + return ctx.Err() case route := <-routeMonitor.RouteUpdates(): if route.Destination.Bits() != 0 { continue } - if routeChanged(route, nexthopv4, nexthopv6, callback) { - break + if routeChanged(route, nexthopv4, nexthopv6) { + return nil } } } } -func routeChanged(route systemops.RouteUpdate, nexthopv4, nexthopv6 systemops.Nexthop, callback func()) bool { +func routeChanged(route systemops.RouteUpdate, nexthopv4, nexthopv6 systemops.Nexthop) bool { intf := "" if route.Interface != nil { intf = route.Interface.Name @@ -51,18 +51,15 @@ func routeChanged(route systemops.RouteUpdate, nexthopv4, nexthopv6 systemops.Ne case systemops.RouteModified: // TODO: get routing table to figure out if our route is affected for modified routes log.Infof("Network monitor: default route changed: via %s, interface %s", route.NextHop, intf) - go callback() return true case systemops.RouteAdded: if route.NextHop.Is4() && route.NextHop != nexthopv4.IP || route.NextHop.Is6() && route.NextHop != nexthopv6.IP { log.Infof("Network monitor: default route added: via %s, interface %s", route.NextHop, intf) - go callback() return true } case systemops.RouteDeleted: if nexthopv4.Intf != nil && route.NextHop == nexthopv4.IP || nexthopv6.Intf != nil && route.NextHop == nexthopv6.IP { log.Infof("Network monitor: default route removed: via %s, interface %s", route.NextHop, intf) - go callback() return true } } diff --git a/client/internal/networkmonitor/monitor.go b/client/internal/networkmonitor/monitor.go index 5475455c6..5896b66b6 100644 --- a/client/internal/networkmonitor/monitor.go +++ b/client/internal/networkmonitor/monitor.go @@ -1,12 +1,27 @@ +//go:build !ios && !android + package networkmonitor import ( "context" "errors" + "fmt" + "net/netip" + "runtime/debug" "sync" + "time" + + "github.com/cenkalti/backoff/v4" + log "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/client/internal/routemanager/systemops" ) -var ErrStopped = errors.New("monitor has been stopped") +const ( + debounceTime = 2 * time.Second +) + +var checkChangeFn = checkChange // NetworkMonitor watches for changes in network configuration. type NetworkMonitor struct { @@ -19,3 +34,99 @@ type NetworkMonitor struct { func New() *NetworkMonitor { return &NetworkMonitor{} } + +// Listen begins monitoring network changes. When a change is detected, this function will return without error. +func (nw *NetworkMonitor) Listen(ctx context.Context) (err error) { + nw.mu.Lock() + if nw.cancel != nil { + nw.mu.Unlock() + return errors.New("network monitor already started") + } + + ctx, nw.cancel = context.WithCancel(ctx) + defer nw.cancel() + nw.wg.Add(1) + nw.mu.Unlock() + + defer nw.wg.Done() + + var nexthop4, nexthop6 systemops.Nexthop + + operation := func() error { + var errv4, errv6 error + nexthop4, errv4 = systemops.GetNextHop(netip.IPv4Unspecified()) + nexthop6, errv6 = systemops.GetNextHop(netip.IPv6Unspecified()) + + if errv4 != nil && errv6 != nil { + return errors.New("failed to get default next hops") + } + + if errv4 == nil { + log.Debugf("Network monitor: IPv4 default route: %s, interface: %s", nexthop4.IP, nexthop4.Intf.Name) + } + if errv6 == nil { + log.Debugf("Network monitor: IPv6 default route: %s, interface: %s", nexthop6.IP, nexthop6.Intf.Name) + } + + // continue if either route was found + return nil + } + + expBackOff := backoff.WithContext(backoff.NewExponentialBackOff(), ctx) + + if err := backoff.Retry(operation, expBackOff); err != nil { + return fmt.Errorf("failed to get default next hops: %w", err) + } + + // recover in case sys ops panic + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic occurred: %v, stack trace: %s", r, debug.Stack()) + } + }() + + event := make(chan struct{}, 1) + go nw.checkChanges(ctx, event, nexthop4, nexthop6) + + // debounce changes + timer := time.NewTimer(0) + timer.Stop() + for { + select { + case <-event: + timer.Reset(debounceTime) + case <-timer.C: + return nil + case <-ctx.Done(): + timer.Stop() + return ctx.Err() + } + } +} + +// Stop stops the network monitor. +func (nw *NetworkMonitor) Stop() { + nw.mu.Lock() + defer nw.mu.Unlock() + + if nw.cancel == nil { + return + } + + nw.cancel() + nw.wg.Wait() +} + +func (nw *NetworkMonitor) checkChanges(ctx context.Context, event chan struct{}, nexthop4 systemops.Nexthop, nexthop6 systemops.Nexthop) { + for { + if err := checkChangeFn(ctx, nexthop4, nexthop6); err != nil { + close(event) + return + } + // prevent blocking + select { + case event <- struct{}{}: + default: + } + } +} diff --git a/client/internal/networkmonitor/monitor_generic.go b/client/internal/networkmonitor/monitor_generic.go deleted file mode 100644 index 19648edba..000000000 --- a/client/internal/networkmonitor/monitor_generic.go +++ /dev/null @@ -1,82 +0,0 @@ -//go:build !ios && !android - -package networkmonitor - -import ( - "context" - "errors" - "fmt" - "net/netip" - "runtime/debug" - - "github.com/cenkalti/backoff/v4" - log "github.com/sirupsen/logrus" - - "github.com/netbirdio/netbird/client/internal/routemanager/systemops" -) - -// Start begins monitoring network changes. When a change is detected, it calls the callback asynchronously and returns. -func (nw *NetworkMonitor) Start(ctx context.Context, callback func()) (err error) { - if ctx.Err() != nil { - return ctx.Err() - } - - nw.mu.Lock() - ctx, nw.cancel = context.WithCancel(ctx) - nw.mu.Unlock() - - nw.wg.Add(1) - defer nw.wg.Done() - - var nexthop4, nexthop6 systemops.Nexthop - - operation := func() error { - var errv4, errv6 error - nexthop4, errv4 = systemops.GetNextHop(netip.IPv4Unspecified()) - nexthop6, errv6 = systemops.GetNextHop(netip.IPv6Unspecified()) - - if errv4 != nil && errv6 != nil { - return errors.New("failed to get default next hops") - } - - if errv4 == nil { - log.Debugf("Network monitor: IPv4 default route: %s, interface: %s", nexthop4.IP, nexthop4.Intf.Name) - } - if errv6 == nil { - log.Debugf("Network monitor: IPv6 default route: %s, interface: %s", nexthop6.IP, nexthop6.Intf.Name) - } - - // continue if either route was found - return nil - } - - expBackOff := backoff.WithContext(backoff.NewExponentialBackOff(), ctx) - - if err := backoff.Retry(operation, expBackOff); err != nil { - return fmt.Errorf("failed to get default next hops: %w", err) - } - - // recover in case sys ops panic - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("panic occurred: %v, stack trace: %s", r, debug.Stack()) - } - }() - - if err := checkChange(ctx, nexthop4, nexthop6, callback); err != nil { - return fmt.Errorf("check change: %w", err) - } - - return nil -} - -// Stop stops the network monitor. -func (nw *NetworkMonitor) Stop() { - nw.mu.Lock() - defer nw.mu.Unlock() - - if nw.cancel != nil { - nw.cancel() - nw.wg.Wait() - } -} diff --git a/client/internal/networkmonitor/monitor_mobile.go b/client/internal/networkmonitor/monitor_mobile.go index c81fad16c..861dbbe3c 100644 --- a/client/internal/networkmonitor/monitor_mobile.go +++ b/client/internal/networkmonitor/monitor_mobile.go @@ -2,10 +2,21 @@ package networkmonitor -import "context" +import ( + "context" + "fmt" +) -func (nw *NetworkMonitor) Start(context.Context, func()) error { - return nil +type NetworkMonitor struct { +} + +// New creates a new network monitor. +func New() *NetworkMonitor { + return &NetworkMonitor{} +} + +func (nw *NetworkMonitor) Listen(_ context.Context) error { + return fmt.Errorf("network monitor not supported on mobile platforms") } func (nw *NetworkMonitor) Stop() { diff --git a/client/internal/networkmonitor/monitor_test.go b/client/internal/networkmonitor/monitor_test.go new file mode 100644 index 000000000..164686689 --- /dev/null +++ b/client/internal/networkmonitor/monitor_test.go @@ -0,0 +1,99 @@ +package networkmonitor + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/netbirdio/netbird/client/internal/routemanager/systemops" +) + +type MocMultiEvent struct { + counter int +} + +func (m *MocMultiEvent) checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error { + if m.counter == 0 { + <-ctx.Done() + return ctx.Err() + } + + time.Sleep(1 * time.Second) + m.counter-- + return nil +} + +func TestNetworkMonitor_Close(t *testing.T) { + checkChangeFn = func(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error { + <-ctx.Done() + return ctx.Err() + } + nw := New() + + var resErr error + done := make(chan struct{}) + go func() { + resErr = nw.Listen(context.Background()) + close(done) + }() + + time.Sleep(1 * time.Second) // wait for the goroutine to start + nw.Stop() + + <-done + if !errors.Is(resErr, context.Canceled) { + t.Errorf("unexpected error: %v", resErr) + } +} + +func TestNetworkMonitor_Event(t *testing.T) { + checkChangeFn = func(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop) error { + timeout, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + select { + case <-ctx.Done(): + return ctx.Err() + case <-timeout.Done(): + return nil + } + } + nw := New() + defer nw.Stop() + + var resErr error + done := make(chan struct{}) + go func() { + resErr = nw.Listen(context.Background()) + close(done) + }() + + <-done + if !errors.Is(resErr, nil) { + t.Errorf("unexpected error: %v", nil) + } +} + +func TestNetworkMonitor_MultiEvent(t *testing.T) { + eventsRepeated := 3 + me := &MocMultiEvent{counter: eventsRepeated} + checkChangeFn = me.checkChange + + nw := New() + defer nw.Stop() + + done := make(chan struct{}) + started := time.Now() + go func() { + if resErr := nw.Listen(context.Background()); resErr != nil { + t.Errorf("unexpected error: %v", resErr) + } + close(done) + }() + + <-done + expectedResponseTime := time.Duration(eventsRepeated)*time.Second + debounceTime + if time.Since(started) < expectedResponseTime { + t.Errorf("unexpected duration: %v", time.Since(started)) + } +} From cc8f6bcaf3c4210690c0d783faf1ad345452aff4 Mon Sep 17 00:00:00 2001 From: Bethuel Mmbaga Date: Mon, 10 Mar 2025 15:54:36 +0300 Subject: [PATCH 25/30] [management] Fix tests circular dependency (#3460) Signed-off-by: bcmmbaga --- go.mod | 2 +- go.sum | 4 +- management/server/account.go | 4 +- ...ccount_manager_interface.go => manager.go} | 37 ++----------------- management/server/account_test.go | 8 ++-- management/server/ephemeral.go | 4 +- management/server/ephemeral_test.go | 2 +- management/server/groups/manager.go | 4 +- management/server/grpcserver.go | 10 ++--- management/server/http/handler.go | 2 +- .../handlers/accounts/accounts_handler.go | 6 +-- .../http/handlers/dns/dns_settings_handler.go | 8 ++-- .../http/handlers/dns/nameservers_handler.go | 6 +-- .../http/handlers/events/events_handler.go | 6 +-- .../http/handlers/groups/groups_handler.go | 6 +-- .../server/http/handlers/networks/handler.go | 6 +-- .../http/handlers/peers/peers_handler.go | 6 +-- .../handlers/policies/geolocations_handler.go | 6 +-- .../handlers/policies/policies_handler.go | 6 +-- .../policies/posture_checks_handler.go | 6 +-- .../http/handlers/routes/routes_handler.go | 6 +-- .../handlers/setup_keys/setupkeys_handler.go | 6 +-- .../server/http/handlers/users/pat_handler.go | 6 +-- .../http/handlers/users/users_handler.go | 6 +-- .../http/testing/testing_tools/tools.go | 2 +- management/server/management_proto_test.go | 5 +-- management/server/mock_server/account_mock.go | 10 ++--- management/server/networks/manager.go | 4 +- .../server/networks/resources/manager.go | 4 +- management/server/networks/routers/manager.go | 4 +- management/server/peer.go | 9 ++--- management/server/settings/manager.go | 7 +++- management/server/token_mgr.go | 10 ++--- management/server/types/peer.go | 37 +++++++++++++++++++ 34 files changed, 132 insertions(+), 123 deletions(-) rename management/server/account/{account_manager_interface.go => manager.go} (85%) create mode 100644 management/server/types/peer.go diff --git a/go.mod b/go.mod index b0b697877..9c7e808af 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/miekg/dns v1.1.59 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/nadoo/ipset v0.5.0 - github.com/netbirdio/management-integrations/integrations v0.0.0-20250309183036-fe518506f9de + github.com/netbirdio/management-integrations/integrations v0.0.0-20250310094048-24724cc8c9c3 github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d github.com/okta/okta-sdk-golang/v2 v2.18.0 github.com/oschwald/maxminddb-golang v1.12.0 diff --git a/go.sum b/go.sum index 20c49ed13..75766027d 100644 --- a/go.sum +++ b/go.sum @@ -529,8 +529,8 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250309183036-fe518506f9de h1:YK4gywy+JKZXu2x/jQPaxvLFs6b5nR97yP9mtjmWru8= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250309183036-fe518506f9de/go.mod h1:gxlShs2JgyGBdGt4ctGVZsShX2Tcv8YFYt9QKr3jNhU= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250310094048-24724cc8c9c3 h1:+DNgrPvpdNZR/UiyFQ7fb8weENmy7rB5S54zIoTPhYE= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250310094048-24724cc8c9c3/go.mod h1:NZ63GQu65YcqarxJxY9p05ukZY16sEmcW9O3GX92T/A= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d h1:bRq5TKgC7Iq20pDiuC54yXaWnAVeS5PdGpSokFTlR28= diff --git a/management/server/account.go b/management/server/account.go index eb4f0cf38..b7574f7c1 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -1391,7 +1391,7 @@ func (am *DefaultAccountManager) SyncAndMarkPeer(ctx context.Context, accountID peerUnlock := am.Store.AcquireWriteLockByUID(ctx, peerPubKey) defer peerUnlock() - peer, netMap, postureChecks, err := am.SyncPeer(ctx, account.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta}, accountID) + peer, netMap, postureChecks, err := am.SyncPeer(ctx, types.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta}, accountID) if err != nil { return nil, nil, nil, fmt.Errorf("error syncing peer: %w", err) } @@ -1431,7 +1431,7 @@ func (am *DefaultAccountManager) SyncPeerMeta(ctx context.Context, peerPubKey st unlockPeer := am.Store.AcquireWriteLockByUID(ctx, peerPubKey) defer unlockPeer() - _, _, _, err = am.SyncPeer(ctx, account.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta, UpdateAccountPeers: true}, accountID) + _, _, _, err = am.SyncPeer(ctx, types.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta, UpdateAccountPeers: true}, accountID) if err != nil { return mapError(ctx, err) } diff --git a/management/server/account/account_manager_interface.go b/management/server/account/manager.go similarity index 85% rename from management/server/account/account_manager_interface.go rename to management/server/account/manager.go index f0e20a9d8..9da85473c 100644 --- a/management/server/account/account_manager_interface.go +++ b/management/server/account/manager.go @@ -22,8 +22,7 @@ import ( type ExternalCacheManager cache.CacheInterface[*idp.UserData] -// nolint -type AccountManager interface { +type Manager interface { GetOrCreateAccountByUser(ctx context.Context, userId, domain string) (*types.Account, error) GetAccount(ctx context.Context, accountID string) (*types.Account, error) CreateSetupKey(ctx context.Context, accountID string, keyName string, keyType types.SetupKeyType, expiresIn time.Duration, @@ -89,8 +88,8 @@ type AccountManager interface { SaveDNSSettings(ctx context.Context, accountID string, userID string, dnsSettingsToSave *types.DNSSettings) error GetPeer(ctx context.Context, accountID, peerID, userID string) (*nbpeer.Peer, error) UpdateAccountSettings(ctx context.Context, accountID, userID string, newSettings *types.Settings) (*types.Account, error) - LoginPeer(ctx context.Context, login PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) // used by peer gRPC API - SyncPeer(ctx context.Context, sync PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) // used by peer gRPC API + LoginPeer(ctx context.Context, login types.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) // used by peer gRPC API + SyncPeer(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) // used by peer gRPC API GetAllConnectedPeers() (map[string]struct{}, error) HasConnectedChannel(peerID string) bool GetExternalCacheManager() ExternalCacheManager @@ -114,33 +113,3 @@ type AccountManager interface { SyncUserJWTGroups(ctx context.Context, userAuth nbcontext.UserAuth) error GetStore() store.Store } - -// PeerSync used as a data object between the gRPC API and AccountManager on Sync request. -type PeerSync struct { - // WireGuardPubKey is a peers WireGuard public key - WireGuardPubKey string - // Meta is the system information passed by peer, must be always present - Meta nbpeer.PeerSystemMeta - // UpdateAccountPeers indicate updating account peers, - // which occurs when the peer's metadata is updated - UpdateAccountPeers bool -} - -// PeerLogin used as a data object between the gRPC API and AccountManager on Login request. -type PeerLogin struct { - // WireGuardPubKey is a peers WireGuard public key - WireGuardPubKey string - // SSHKey is a peer's ssh key. Can be empty (e.g., old version do not provide it, or this feature is disabled) - SSHKey string - // Meta is the system information passed by peer, must be always present. - Meta nbpeer.PeerSystemMeta - // UserID indicates that JWT was used to log in, and it was valid. Can be empty when SetupKey is used or auth is not required. - UserID string - // SetupKey references to a server.SetupKey to log in. Can be empty when UserID is used or auth is not required. - SetupKey string - // ConnectionIP is the real IP of the peer - ConnectionIP net.IP - - // ExtraDNSLabels is a list of extra DNS labels that the peer wants to use - ExtraDNSLabels []string -} diff --git a/management/server/account_test.go b/management/server/account_test.go index 7c11048a4..4e47ccc60 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -38,7 +38,7 @@ import ( "github.com/netbirdio/netbird/route" ) -func verifyCanAddPeerToAccount(t *testing.T, manager nbAccount.AccountManager, account *types.Account, userID string) { +func verifyCanAddPeerToAccount(t *testing.T, manager nbAccount.Manager, account *types.Account, userID string) { t.Helper() peer := &nbpeer.Peer{ Key: "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8=", @@ -1405,7 +1405,7 @@ func TestAccountManager_DeletePeer(t *testing.T) { assert.Equal(t, peer.IP.String(), fmt.Sprint(ev.Meta["ip"])) } -func getEvent(t *testing.T, accountID string, manager nbAccount.AccountManager, eventType activity.Activity) *activity.Event { +func getEvent(t *testing.T, accountID string, manager nbAccount.Manager, eventType activity.Activity) *activity.Event { t.Helper() for { select { @@ -3026,7 +3026,7 @@ func BenchmarkLoginPeer_ExistingPeer(b *testing.B) { b.ResetTimer() start := time.Now() for i := 0; i < b.N; i++ { - _, _, _, err := manager.LoginPeer(context.Background(), nbAccount.PeerLogin{ + _, _, _, err := manager.LoginPeer(context.Background(), types.PeerLogin{ WireGuardPubKey: account.Peers["peer-1"].Key, SSHKey: "someKey", Meta: nbpeer.PeerSystemMeta{Hostname: strconv.Itoa(i)}, @@ -3101,7 +3101,7 @@ func BenchmarkLoginPeer_NewPeer(b *testing.B) { b.ResetTimer() start := time.Now() for i := 0; i < b.N; i++ { - _, _, _, err := manager.LoginPeer(context.Background(), nbAccount.PeerLogin{ + _, _, _, err := manager.LoginPeer(context.Background(), types.PeerLogin{ WireGuardPubKey: "some-new-key" + strconv.Itoa(i), SSHKey: "someKey", Meta: nbpeer.PeerSystemMeta{Hostname: strconv.Itoa(i)}, diff --git a/management/server/ephemeral.go b/management/server/ephemeral.go index 52ab450d7..3cb9b7536 100644 --- a/management/server/ephemeral.go +++ b/management/server/ephemeral.go @@ -35,7 +35,7 @@ type ephemeralPeer struct { // automatically. Inactivity means the peer disconnected from the Management server. type EphemeralManager struct { store store.Store - accountManager nbAccount.AccountManager + accountManager nbAccount.Manager headPeer *ephemeralPeer tailPeer *ephemeralPeer @@ -44,7 +44,7 @@ type EphemeralManager struct { } // NewEphemeralManager instantiate new EphemeralManager -func NewEphemeralManager(store store.Store, accountManager nbAccount.AccountManager) *EphemeralManager { +func NewEphemeralManager(store store.Store, accountManager nbAccount.Manager) *EphemeralManager { return &EphemeralManager{ store: store, accountManager: accountManager, diff --git a/management/server/ephemeral_test.go b/management/server/ephemeral_test.go index 76e60f2c6..38477f7a8 100644 --- a/management/server/ephemeral_test.go +++ b/management/server/ephemeral_test.go @@ -28,7 +28,7 @@ func (s *MockStore) GetAllEphemeralPeers(_ context.Context, _ store.LockingStren } type MocAccountManager struct { - nbAccount.AccountManager + nbAccount.Manager store *MockStore } diff --git a/management/server/groups/manager.go b/management/server/groups/manager.go index d5431be7b..27698a085 100644 --- a/management/server/groups/manager.go +++ b/management/server/groups/manager.go @@ -24,13 +24,13 @@ type Manager interface { type managerImpl struct { store store.Store permissionsManager permissions.Manager - accountManager account.AccountManager + accountManager account.Manager } type mockManager struct { } -func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager account.AccountManager) Manager { +func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager account.Manager) Manager { return &managerImpl{ store: store, permissionsManager: permissionsManager, diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 4e72bd96f..97dd7e0b6 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -12,13 +12,13 @@ import ( pb "github.com/golang/protobuf/proto" // nolint "github.com/golang/protobuf/ptypes/timestamp" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip" - "github.com/netbirdio/management-integrations/integrations" log "github.com/sirupsen/logrus" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc/codes" "google.golang.org/grpc/peer" "google.golang.org/grpc/status" + integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" "github.com/netbirdio/netbird/encryption" "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server/account" @@ -35,7 +35,7 @@ import ( // GRPCServer an instance of a Management gRPC API server type GRPCServer struct { - accountManager account.AccountManager + accountManager account.Manager settingsManager settings.Manager wgKey wgtypes.Key proto.UnimplementedManagementServiceServer @@ -52,7 +52,7 @@ type GRPCServer struct { func NewServer( ctx context.Context, config *Config, - accountManager account.AccountManager, + accountManager account.Manager, settingsManager settings.Manager, peersUpdateManager *PeersUpdateManager, secretsManager SecretsManager, @@ -460,7 +460,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p sshKey = loginReq.GetPeerKeys().GetSshPubKey() } - peer, netMap, postureChecks, err := s.accountManager.LoginPeer(ctx, account.PeerLogin{ + peer, netMap, postureChecks, err := s.accountManager.LoginPeer(ctx, types.PeerLogin{ WireGuardPubKey: peerKey.String(), SSHKey: string(sshKey), Meta: extractPeerMeta(ctx, loginReq.GetMeta()), @@ -605,7 +605,7 @@ func toNetbirdConfig(config *Config, turnCredentials *Token, relayToken *Token, Relay: relayCfg, } - integrations.ExtendNetBirdConfig(nbConfig, extraSettings) + integrationsConfig.ExtendNetBirdConfig(nbConfig, extraSettings) return nbConfig } diff --git a/management/server/http/handler.go b/management/server/http/handler.go index 6a3cfae70..e4cc8585a 100644 --- a/management/server/http/handler.go +++ b/management/server/http/handler.go @@ -43,7 +43,7 @@ const apiPrefix = "/api" // NewAPIHandler creates the Management service HTTP API handler registering all the available endpoints. func NewAPIHandler( ctx context.Context, - accountManager account.AccountManager, + accountManager account.Manager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, diff --git a/management/server/http/handlers/accounts/accounts_handler.go b/management/server/http/handlers/accounts/accounts_handler.go index b76d28094..054a9e77a 100644 --- a/management/server/http/handlers/accounts/accounts_handler.go +++ b/management/server/http/handlers/accounts/accounts_handler.go @@ -18,11 +18,11 @@ import ( // handler is a handler that handles the server.Account HTTP endpoints type handler struct { - accountManager account.AccountManager + accountManager account.Manager settingsManager settings.Manager } -func AddEndpoints(accountManager account.AccountManager, settingsManager settings.Manager, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, settingsManager settings.Manager, router *mux.Router) { accountsHandler := newHandler(accountManager, settingsManager) router.HandleFunc("/accounts/{accountId}", accountsHandler.updateAccount).Methods("PUT", "OPTIONS") router.HandleFunc("/accounts/{accountId}", accountsHandler.deleteAccount).Methods("DELETE", "OPTIONS") @@ -30,7 +30,7 @@ func AddEndpoints(accountManager account.AccountManager, settingsManager setting } // newHandler creates a new handler HTTP handler -func newHandler(accountManager account.AccountManager, settingsManager settings.Manager) *handler { +func newHandler(accountManager account.Manager, settingsManager settings.Manager) *handler { return &handler{ accountManager: accountManager, settingsManager: settingsManager, diff --git a/management/server/http/handlers/dns/dns_settings_handler.go b/management/server/http/handlers/dns/dns_settings_handler.go index 85be8e707..60822c883 100644 --- a/management/server/http/handlers/dns/dns_settings_handler.go +++ b/management/server/http/handlers/dns/dns_settings_handler.go @@ -16,22 +16,22 @@ import ( // dnsSettingsHandler is a handler that returns the DNS settings of the account type dnsSettingsHandler struct { - accountManager account.AccountManager + accountManager account.Manager } -func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, router *mux.Router) { addDNSSettingEndpoint(accountManager, router) addDNSNameserversEndpoint(accountManager, router) } -func addDNSSettingEndpoint(accountManager account.AccountManager, router *mux.Router) { +func addDNSSettingEndpoint(accountManager account.Manager, router *mux.Router) { dnsSettingsHandler := newDNSSettingsHandler(accountManager) router.HandleFunc("/dns/settings", dnsSettingsHandler.getDNSSettings).Methods("GET", "OPTIONS") router.HandleFunc("/dns/settings", dnsSettingsHandler.updateDNSSettings).Methods("PUT", "OPTIONS") } // newDNSSettingsHandler returns a new instance of dnsSettingsHandler handler -func newDNSSettingsHandler(accountManager account.AccountManager) *dnsSettingsHandler { +func newDNSSettingsHandler(accountManager account.Manager) *dnsSettingsHandler { return &dnsSettingsHandler{accountManager: accountManager} } diff --git a/management/server/http/handlers/dns/nameservers_handler.go b/management/server/http/handlers/dns/nameservers_handler.go index 0bba95988..970be6d8a 100644 --- a/management/server/http/handlers/dns/nameservers_handler.go +++ b/management/server/http/handlers/dns/nameservers_handler.go @@ -18,10 +18,10 @@ import ( // nameserversHandler is the nameserver group handler of the account type nameserversHandler struct { - accountManager account.AccountManager + accountManager account.Manager } -func addDNSNameserversEndpoint(accountManager account.AccountManager, router *mux.Router) { +func addDNSNameserversEndpoint(accountManager account.Manager, router *mux.Router) { nameserversHandler := newNameserversHandler(accountManager) router.HandleFunc("/dns/nameservers", nameserversHandler.getAllNameservers).Methods("GET", "OPTIONS") router.HandleFunc("/dns/nameservers", nameserversHandler.createNameserverGroup).Methods("POST", "OPTIONS") @@ -31,7 +31,7 @@ func addDNSNameserversEndpoint(accountManager account.AccountManager, router *mu } // newNameserversHandler returns a new instance of nameserversHandler handler -func newNameserversHandler(accountManager account.AccountManager) *nameserversHandler { +func newNameserversHandler(accountManager account.Manager) *nameserversHandler { return &nameserversHandler{accountManager: accountManager} } diff --git a/management/server/http/handlers/events/events_handler.go b/management/server/http/handlers/events/events_handler.go index 984201ac0..7497b0fef 100644 --- a/management/server/http/handlers/events/events_handler.go +++ b/management/server/http/handlers/events/events_handler.go @@ -17,16 +17,16 @@ import ( // handler HTTP handler type handler struct { - accountManager account.AccountManager + accountManager account.Manager } -func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, router *mux.Router) { eventsHandler := newHandler(accountManager) router.HandleFunc("/events", eventsHandler.getAllEvents).Methods("GET", "OPTIONS") } // newHandler creates a new events handler -func newHandler(accountManager account.AccountManager) *handler { +func newHandler(accountManager account.Manager) *handler { return &handler{accountManager: accountManager} } diff --git a/management/server/http/handlers/groups/groups_handler.go b/management/server/http/handlers/groups/groups_handler.go index 012987994..667095018 100644 --- a/management/server/http/handlers/groups/groups_handler.go +++ b/management/server/http/handlers/groups/groups_handler.go @@ -19,10 +19,10 @@ import ( // handler is a handler that returns groups of the account type handler struct { - accountManager account.AccountManager + accountManager account.Manager } -func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, router *mux.Router) { groupsHandler := newHandler(accountManager) router.HandleFunc("/groups", groupsHandler.getAllGroups).Methods("GET", "OPTIONS") router.HandleFunc("/groups", groupsHandler.createGroup).Methods("POST", "OPTIONS") @@ -32,7 +32,7 @@ func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { } // newHandler creates a new groups handler -func newHandler(accountManager account.AccountManager) *handler { +func newHandler(accountManager account.Manager) *handler { return &handler{ accountManager: accountManager, } diff --git a/management/server/http/handlers/networks/handler.go b/management/server/http/handlers/networks/handler.go index 319b8ca45..1c06512f6 100644 --- a/management/server/http/handlers/networks/handler.go +++ b/management/server/http/handlers/networks/handler.go @@ -28,12 +28,12 @@ type handler struct { networksManager networks.Manager resourceManager resources.Manager routerManager routers.Manager - accountManager account.AccountManager + accountManager account.Manager groupsManager groups.Manager } -func AddEndpoints(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager account.AccountManager, router *mux.Router) { +func AddEndpoints(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager account.Manager, router *mux.Router) { addRouterEndpoints(routerManager, router) addResourceEndpoints(resourceManager, groupsManager, router) @@ -45,7 +45,7 @@ func AddEndpoints(networksManager networks.Manager, resourceManager resources.Ma router.HandleFunc("/networks/{networkId}", networksHandler.deleteNetwork).Methods("DELETE", "OPTIONS") } -func newHandler(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager account.AccountManager) *handler { +func newHandler(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager account.Manager) *handler { return &handler{ networksManager: networksManager, resourceManager: resourceManager, diff --git a/management/server/http/handlers/peers/peers_handler.go b/management/server/http/handlers/peers/peers_handler.go index 4bf6e4830..9342d84a3 100644 --- a/management/server/http/handlers/peers/peers_handler.go +++ b/management/server/http/handlers/peers/peers_handler.go @@ -21,10 +21,10 @@ import ( // Handler is a handler that returns peers of the account type Handler struct { - accountManager account.AccountManager + accountManager account.Manager } -func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, router *mux.Router) { peersHandler := NewHandler(accountManager) router.HandleFunc("/peers", peersHandler.GetAllPeers).Methods("GET", "OPTIONS") router.HandleFunc("/peers/{peerId}", peersHandler.HandlePeer). @@ -33,7 +33,7 @@ func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { } // NewHandler creates a new peers Handler -func NewHandler(accountManager account.AccountManager) *Handler { +func NewHandler(accountManager account.Manager) *Handler { return &Handler{ accountManager: accountManager, } diff --git a/management/server/http/handlers/policies/geolocations_handler.go b/management/server/http/handlers/policies/geolocations_handler.go index 818230e44..fb19887dc 100644 --- a/management/server/http/handlers/policies/geolocations_handler.go +++ b/management/server/http/handlers/policies/geolocations_handler.go @@ -20,18 +20,18 @@ var ( // geolocationsHandler is a handler that returns locations. type geolocationsHandler struct { - accountManager account.AccountManager + accountManager account.Manager geolocationManager geolocation.Geolocation } -func addLocationsEndpoint(accountManager account.AccountManager, locationManager geolocation.Geolocation, router *mux.Router) { +func addLocationsEndpoint(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) { locationHandler := newGeolocationsHandlerHandler(accountManager, locationManager) router.HandleFunc("/locations/countries", locationHandler.getAllCountries).Methods("GET", "OPTIONS") router.HandleFunc("/locations/countries/{country}/cities", locationHandler.getCitiesByCountry).Methods("GET", "OPTIONS") } // newGeolocationsHandlerHandler creates a new Geolocations handler -func newGeolocationsHandlerHandler(accountManager account.AccountManager, geolocationManager geolocation.Geolocation) *geolocationsHandler { +func newGeolocationsHandlerHandler(accountManager account.Manager, geolocationManager geolocation.Geolocation) *geolocationsHandler { return &geolocationsHandler{ accountManager: accountManager, geolocationManager: geolocationManager, diff --git a/management/server/http/handlers/policies/policies_handler.go b/management/server/http/handlers/policies/policies_handler.go index b76f5b1bd..01a09842a 100644 --- a/management/server/http/handlers/policies/policies_handler.go +++ b/management/server/http/handlers/policies/policies_handler.go @@ -18,10 +18,10 @@ import ( // handler is a handler that returns policy of the account type handler struct { - accountManager account.AccountManager + accountManager account.Manager } -func AddEndpoints(accountManager account.AccountManager, locationManager geolocation.Geolocation, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) { policiesHandler := newHandler(accountManager) router.HandleFunc("/policies", policiesHandler.getAllPolicies).Methods("GET", "OPTIONS") router.HandleFunc("/policies", policiesHandler.createPolicy).Methods("POST", "OPTIONS") @@ -32,7 +32,7 @@ func AddEndpoints(accountManager account.AccountManager, locationManager geoloca } // newHandler creates a new policies handler -func newHandler(accountManager account.AccountManager) *handler { +func newHandler(accountManager account.Manager) *handler { return &handler{ accountManager: accountManager, } diff --git a/management/server/http/handlers/policies/posture_checks_handler.go b/management/server/http/handlers/policies/posture_checks_handler.go index be099d7f2..b99649dbc 100644 --- a/management/server/http/handlers/policies/posture_checks_handler.go +++ b/management/server/http/handlers/policies/posture_checks_handler.go @@ -17,11 +17,11 @@ import ( // postureChecksHandler is a handler that returns posture checks of the account. type postureChecksHandler struct { - accountManager account.AccountManager + accountManager account.Manager geolocationManager geolocation.Geolocation } -func addPostureCheckEndpoint(accountManager account.AccountManager, locationManager geolocation.Geolocation, router *mux.Router) { +func addPostureCheckEndpoint(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) { postureCheckHandler := newPostureChecksHandler(accountManager, locationManager) router.HandleFunc("/posture-checks", postureCheckHandler.getAllPostureChecks).Methods("GET", "OPTIONS") router.HandleFunc("/posture-checks", postureCheckHandler.createPostureCheck).Methods("POST", "OPTIONS") @@ -32,7 +32,7 @@ func addPostureCheckEndpoint(accountManager account.AccountManager, locationMana } // newPostureChecksHandler creates a new PostureChecks handler -func newPostureChecksHandler(accountManager account.AccountManager, geolocationManager geolocation.Geolocation) *postureChecksHandler { +func newPostureChecksHandler(accountManager account.Manager, geolocationManager geolocation.Geolocation) *postureChecksHandler { return &postureChecksHandler{ accountManager: accountManager, geolocationManager: geolocationManager, diff --git a/management/server/http/handlers/routes/routes_handler.go b/management/server/http/handlers/routes/routes_handler.go index d6aa52c5e..0f1c37eb7 100644 --- a/management/server/http/handlers/routes/routes_handler.go +++ b/management/server/http/handlers/routes/routes_handler.go @@ -21,10 +21,10 @@ const failedToConvertRoute = "failed to convert route to response: %v" // handler is the routes handler of the account type handler struct { - accountManager account.AccountManager + accountManager account.Manager } -func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, router *mux.Router) { routesHandler := newHandler(accountManager) router.HandleFunc("/routes", routesHandler.getAllRoutes).Methods("GET", "OPTIONS") router.HandleFunc("/routes", routesHandler.createRoute).Methods("POST", "OPTIONS") @@ -34,7 +34,7 @@ func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { } // newHandler returns a new instance of routes handler -func newHandler(accountManager account.AccountManager) *handler { +func newHandler(accountManager account.Manager) *handler { return &handler{ accountManager: accountManager, } diff --git a/management/server/http/handlers/setup_keys/setupkeys_handler.go b/management/server/http/handlers/setup_keys/setupkeys_handler.go index bebbe1f16..38ba86fb1 100644 --- a/management/server/http/handlers/setup_keys/setupkeys_handler.go +++ b/management/server/http/handlers/setup_keys/setupkeys_handler.go @@ -18,10 +18,10 @@ import ( // handler is a handler that returns a list of setup keys of the account type handler struct { - accountManager account.AccountManager + accountManager account.Manager } -func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, router *mux.Router) { keysHandler := newHandler(accountManager) router.HandleFunc("/setup-keys", keysHandler.getAllSetupKeys).Methods("GET", "OPTIONS") router.HandleFunc("/setup-keys", keysHandler.createSetupKey).Methods("POST", "OPTIONS") @@ -31,7 +31,7 @@ func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { } // newHandler creates a new setup key handler -func newHandler(accountManager account.AccountManager) *handler { +func newHandler(accountManager account.Manager) *handler { return &handler{ accountManager: accountManager, } diff --git a/management/server/http/handlers/users/pat_handler.go b/management/server/http/handlers/users/pat_handler.go index ba8fcd3ed..90913eac1 100644 --- a/management/server/http/handlers/users/pat_handler.go +++ b/management/server/http/handlers/users/pat_handler.go @@ -16,10 +16,10 @@ import ( // patHandler is the nameserver group handler of the account type patHandler struct { - accountManager account.AccountManager + accountManager account.Manager } -func addUsersTokensEndpoint(accountManager account.AccountManager, router *mux.Router) { +func addUsersTokensEndpoint(accountManager account.Manager, router *mux.Router) { tokenHandler := newPATsHandler(accountManager) router.HandleFunc("/users/{userId}/tokens", tokenHandler.getAllTokens).Methods("GET", "OPTIONS") router.HandleFunc("/users/{userId}/tokens", tokenHandler.createToken).Methods("POST", "OPTIONS") @@ -28,7 +28,7 @@ func addUsersTokensEndpoint(accountManager account.AccountManager, router *mux.R } // newPATsHandler creates a new patHandler HTTP handler -func newPATsHandler(accountManager account.AccountManager) *patHandler { +func newPATsHandler(accountManager account.Manager) *patHandler { return &patHandler{ accountManager: accountManager, } diff --git a/management/server/http/handlers/users/users_handler.go b/management/server/http/handlers/users/users_handler.go index 6dcd4ea69..19f56c464 100644 --- a/management/server/http/handlers/users/users_handler.go +++ b/management/server/http/handlers/users/users_handler.go @@ -19,10 +19,10 @@ import ( // handler is a handler that returns users of the account type handler struct { - accountManager account.AccountManager + accountManager account.Manager } -func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { +func AddEndpoints(accountManager account.Manager, router *mux.Router) { userHandler := newHandler(accountManager) router.HandleFunc("/users", userHandler.getAllUsers).Methods("GET", "OPTIONS") router.HandleFunc("/users/{userId}", userHandler.updateUser).Methods("PUT", "OPTIONS") @@ -33,7 +33,7 @@ func AddEndpoints(accountManager account.AccountManager, router *mux.Router) { } // newHandler creates a new UsersHandler HTTP handler -func newHandler(accountManager account.AccountManager) *handler { +func newHandler(accountManager account.Manager) *handler { return &handler{ accountManager: accountManager, } diff --git a/management/server/http/testing/testing_tools/tools.go b/management/server/http/testing/testing_tools/tools.go index 37b9a3023..5d1498c33 100644 --- a/management/server/http/testing/testing_tools/tools.go +++ b/management/server/http/testing/testing_tools/tools.go @@ -94,7 +94,7 @@ type PerformanceMetrics struct { MaxMsPerOpCICD float64 } -func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *server.UpdateMessage, validateUpdate bool) (http.Handler, account.AccountManager, chan struct{}) { +func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *server.UpdateMessage, validateUpdate bool) (http.Handler, account.Manager, chan struct{}) { store, cleanup, err := store.NewTestStoreFromSQL(context.Background(), sqlFile, t.TempDir()) if err != nil { t.Fatalf("Failed to create test store: %v", err) diff --git a/management/server/management_proto_test.go b/management/server/management_proto_test.go index 87ab1f433..9bbc9d7a6 100644 --- a/management/server/management_proto_test.go +++ b/management/server/management_proto_test.go @@ -22,7 +22,6 @@ import ( "github.com/netbirdio/netbird/encryption" "github.com/netbirdio/netbird/formatter/hook" mgmtProto "github.com/netbirdio/netbird/management/proto" - nbAccount "github.com/netbirdio/netbird/management/server/account" "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/settings" @@ -741,7 +740,7 @@ func Test_LoginPerformance(t *testing.T) { NetbirdVersion: "", } - peerLogin := nbAccount.PeerLogin{ + peerLogin := types.PeerLogin{ WireGuardPubKey: key.String(), SSHKey: "random", Meta: extractPeerMeta(context.Background(), meta), @@ -766,7 +765,7 @@ func Test_LoginPerformance(t *testing.T) { messageCalls = append(messageCalls, login) mu.Unlock() - go func(peerLogin nbAccount.PeerLogin, counterStart *int32) { + go func(peerLogin types.PeerLogin, counterStart *int32) { defer wgPeer.Done() _, _, _, err = am.LoginPeer(context.Background(), peerLogin) if err != nil { diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index e23d198b6..cb8d598f8 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -22,7 +22,7 @@ import ( "github.com/netbirdio/netbird/route" ) -var _ account.AccountManager = (*MockAccountManager)(nil) +var _ account.Manager = (*MockAccountManager)(nil) type MockAccountManager struct { GetOrCreateAccountByUserFunc func(ctx context.Context, userId, domain string) (*types.Account, error) @@ -90,8 +90,8 @@ type MockAccountManager struct { SaveDNSSettingsFunc func(ctx context.Context, accountID, userID string, dnsSettingsToSave *types.DNSSettings) error GetPeerFunc func(ctx context.Context, accountID, peerID, userID string) (*nbpeer.Peer, error) UpdateAccountSettingsFunc func(ctx context.Context, accountID, userID string, newSettings *types.Settings) (*types.Account, error) - LoginPeerFunc func(ctx context.Context, login account.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) - SyncPeerFunc func(ctx context.Context, sync account.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) + LoginPeerFunc func(ctx context.Context, login types.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) + SyncPeerFunc func(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) InviteUserFunc func(ctx context.Context, accountID string, initiatorUserID string, targetUserEmail string) error GetAllConnectedPeersFunc func() (map[string]struct{}, error) HasConnectedChannelFunc func(peerID string) bool @@ -663,7 +663,7 @@ func (am *MockAccountManager) UpdateAccountSettings(ctx context.Context, account } // LoginPeer mocks LoginPeer of the AccountManager interface -func (am *MockAccountManager) LoginPeer(ctx context.Context, login account.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { +func (am *MockAccountManager) LoginPeer(ctx context.Context, login types.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { if am.LoginPeerFunc != nil { return am.LoginPeerFunc(ctx, login) } @@ -671,7 +671,7 @@ func (am *MockAccountManager) LoginPeer(ctx context.Context, login account.PeerL } // SyncPeer mocks SyncPeer of the AccountManager interface -func (am *MockAccountManager) SyncPeer(ctx context.Context, sync account.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { +func (am *MockAccountManager) SyncPeer(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { if am.SyncPeerFunc != nil { return am.SyncPeerFunc(ctx, sync, accountID) } diff --git a/management/server/networks/manager.go b/management/server/networks/manager.go index 43f0ed0f8..609b68918 100644 --- a/management/server/networks/manager.go +++ b/management/server/networks/manager.go @@ -26,7 +26,7 @@ type Manager interface { type managerImpl struct { store store.Store - accountManager account.AccountManager + accountManager account.Manager permissionsManager permissions.Manager resourcesManager resources.Manager routersManager routers.Manager @@ -35,7 +35,7 @@ type managerImpl struct { type mockManager struct { } -func NewManager(store store.Store, permissionsManager permissions.Manager, resourceManager resources.Manager, routersManager routers.Manager, accountManager account.AccountManager) Manager { +func NewManager(store store.Store, permissionsManager permissions.Manager, resourceManager resources.Manager, routersManager routers.Manager, accountManager account.Manager) Manager { return &managerImpl{ store: store, permissionsManager: permissionsManager, diff --git a/management/server/networks/resources/manager.go b/management/server/networks/resources/manager.go index 8cf09e561..acaacbfb9 100644 --- a/management/server/networks/resources/manager.go +++ b/management/server/networks/resources/manager.go @@ -31,13 +31,13 @@ type managerImpl struct { store store.Store permissionsManager permissions.Manager groupsManager groups.Manager - accountManager account.AccountManager + accountManager account.Manager } type mockManager struct { } -func NewManager(store store.Store, permissionsManager permissions.Manager, groupsManager groups.Manager, accountManager account.AccountManager) Manager { +func NewManager(store store.Store, permissionsManager permissions.Manager, groupsManager groups.Manager, accountManager account.Manager) Manager { return &managerImpl{ store: store, permissionsManager: permissionsManager, diff --git a/management/server/networks/routers/manager.go b/management/server/networks/routers/manager.go index 793efcf4f..595fffd97 100644 --- a/management/server/networks/routers/manager.go +++ b/management/server/networks/routers/manager.go @@ -29,13 +29,13 @@ type Manager interface { type managerImpl struct { store store.Store permissionsManager permissions.Manager - accountManager account.AccountManager + accountManager account.Manager } type mockManager struct { } -func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager account.AccountManager) Manager { +func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager account.Manager) Manager { return &managerImpl{ store: store, permissionsManager: permissionsManager, diff --git a/management/server/peer.go b/management/server/peer.go index a2b6e2165..4e70fe6e3 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -16,7 +16,6 @@ import ( "golang.org/x/exp/maps" "github.com/netbirdio/netbird/management/domain" - "github.com/netbirdio/netbird/management/server/account" "github.com/netbirdio/netbird/management/server/geolocation" "github.com/netbirdio/netbird/management/server/idp" @@ -680,7 +679,7 @@ func getFreeIP(ctx context.Context, transaction store.Store, accountID string) ( } // SyncPeer checks whether peer is eligible for receiving NetworkMap (authenticated) and returns its NetworkMap if eligible -func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync account.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { +func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { start := time.Now() defer func() { log.WithContext(ctx).Debugf("SyncPeer: took %v", time.Since(start)) @@ -755,7 +754,7 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync account.Peer return am.getValidatedPeerWithMap(ctx, peerNotValid, accountID, peer) } -func (am *DefaultAccountManager) handlePeerLoginNotFound(ctx context.Context, login account.PeerLogin, err error) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { +func (am *DefaultAccountManager) handlePeerLoginNotFound(ctx context.Context, login types.PeerLogin, err error) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { if errStatus, ok := status.FromError(err); ok && errStatus.Type() == status.NotFound { // we couldn't find this peer by its public key which can mean that peer hasn't been registered yet. // Try registering it. @@ -775,7 +774,7 @@ func (am *DefaultAccountManager) handlePeerLoginNotFound(ctx context.Context, lo // LoginPeer logs in or registers a peer. // If peer doesn't exist the function checks whether a setup key or a user is present and registers a new peer if so. -func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login account.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { +func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login types.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) { accountID, err := am.Store.GetAccountIDByPeerPubKey(ctx, login.WireGuardPubKey) if err != nil { return am.handlePeerLoginNotFound(ctx, login, err) @@ -965,7 +964,7 @@ func processPeerPostureChecks(ctx context.Context, transaction store.Store, poli // The NetBird client doesn't have a way to check if the peer needs login besides sending a login request // with no JWT token and usually no setup-key. As the client can send up to two login request to check if it is expired // and before starting the engine, we do the checks without an account lock to avoid piling up requests. -func (am *DefaultAccountManager) checkIFPeerNeedsLoginWithoutLock(ctx context.Context, accountID string, login account.PeerLogin) error { +func (am *DefaultAccountManager) checkIFPeerNeedsLoginWithoutLock(ctx context.Context, accountID string, login types.PeerLogin) error { peer, err := am.Store.GetPeerByPeerPubKey(ctx, store.LockingStrengthShare, login.WireGuardPubKey) if err != nil { return err diff --git a/management/server/settings/manager.go b/management/server/settings/manager.go index 0bf0f63b3..9cc5a0b32 100644 --- a/management/server/settings/manager.go +++ b/management/server/settings/manager.go @@ -26,7 +26,8 @@ type managerImpl struct { } type ManagerMock struct { - GetSettingsFunc func(ctx context.Context, accountID, userID string) (*types.Settings, error) + GetSettingsFunc func(ctx context.Context, accountID, userID string) (*types.Settings, error) + GetExtraSettingsFunc func(ctx context.Context, accountID string) (*types.ExtraSettings, error) } func NewManager(store store.Store, userManager users.Manager, extraSettingsManager extra_settings.Manager) Manager { @@ -117,6 +118,10 @@ func (m *ManagerMock) SetGetSettingsFunc(f func(ctx context.Context, accountID, } func (m *ManagerMock) GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error) { + if m.GetExtraSettingsFunc != nil { + return m.GetExtraSettingsFunc(ctx, accountID) + } + return &types.ExtraSettings{}, nil } diff --git a/management/server/token_mgr.go b/management/server/token_mgr.go index d3e7a3253..2bd59b6e1 100644 --- a/management/server/token_mgr.go +++ b/management/server/token_mgr.go @@ -9,12 +9,12 @@ import ( "sync" "time" - "github.com/netbirdio/management-integrations/integrations" - log "github.com/sirupsen/logrus" - "github.com/netbirdio/netbird/management/proto" auth "github.com/netbirdio/netbird/relay/auth/hmac" authv2 "github.com/netbirdio/netbird/relay/auth/hmac/v2" + log "github.com/sirupsen/logrus" + + integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" ) const defaultDuration = 12 * time.Hour @@ -217,7 +217,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Cont } } - integrations.ExtendNetBirdConfig(update.NetbirdConfig, nil) + integrationsConfig.ExtendNetBirdConfig(update.NetbirdConfig, nil) log.WithContext(ctx).Debugf("sending new TURN credentials to peer %s", peerID) m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update}) @@ -241,7 +241,7 @@ func (m *TimeBasedAuthSecretsManager) pushNewRelayTokens(ctx context.Context, pe }, } - integrations.ExtendNetBirdConfig(update.NetbirdConfig, nil) + integrationsConfig.ExtendNetBirdConfig(update.NetbirdConfig, nil) log.WithContext(ctx).Debugf("sending new relay credentials to peer %s", peerID) m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update}) diff --git a/management/server/types/peer.go b/management/server/types/peer.go new file mode 100644 index 000000000..15d343793 --- /dev/null +++ b/management/server/types/peer.go @@ -0,0 +1,37 @@ +package types + +import ( + "net" + + nbpeer "github.com/netbirdio/netbird/management/server/peer" +) + +// PeerSync used as a data object between the gRPC API and Manager on Sync request. +type PeerSync struct { + // WireGuardPubKey is a peers WireGuard public key + WireGuardPubKey string + // Meta is the system information passed by peer, must be always present + Meta nbpeer.PeerSystemMeta + // UpdateAccountPeers indicate updating account peers, + // which occurs when the peer's metadata is updated + UpdateAccountPeers bool +} + +// PeerLogin used as a data object between the gRPC API and Manager on Login request. +type PeerLogin struct { + // WireGuardPubKey is a peers WireGuard public key + WireGuardPubKey string + // SSHKey is a peer's ssh key. Can be empty (e.g., old version do not provide it, or this feature is disabled) + SSHKey string + // Meta is the system information passed by peer, must be always present. + Meta nbpeer.PeerSystemMeta + // UserID indicates that JWT was used to log in, and it was valid. Can be empty when SetupKey is used or auth is not required. + UserID string + // SetupKey references to a server.SetupKey to log in. Can be empty when UserID is used or auth is not required. + SetupKey string + // ConnectionIP is the real IP of the peer + ConnectionIP net.IP + + // ExtraDNSLabels is a list of extra DNS labels that the peer wants to use + ExtraDNSLabels []string +} From 80ceb801974a02353d380676edc3c9b6e69040d3 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Mon, 10 Mar 2025 13:59:21 +0100 Subject: [PATCH 26/30] [client] Ignore candidates that are part of the the wireguard subnet (#3472) --- client/firewall/iface.go | 3 ++- client/firewall/iptables/manager_linux.go | 4 ++-- client/firewall/iptables/manager_linux_test.go | 18 +++++++++--------- client/firewall/iptables/state_linux.go | 11 +++++------ client/firewall/nftables/manager_linux.go | 4 ++-- client/firewall/nftables/manager_linux_test.go | 14 +++++++------- client/firewall/nftables/state_linux.go | 11 +++++------ client/firewall/uspfilter/common/iface.go | 4 ++-- client/firewall/uspfilter/localip_test.go | 18 +++++++++--------- .../uspfilter/uspfilter_filter_test.go | 10 +++++----- client/firewall/uspfilter/uspfilter_test.go | 12 ++++++------ client/iface/bind/ice_bind.go | 13 +++++++++---- client/iface/bind/udp_mux_universal.go | 10 ++++++++++ client/iface/device.go | 5 +++-- client/iface/device/device_android.go | 9 +++++---- client/iface/device/device_darwin.go | 9 +++++---- client/iface/device/device_ios.go | 9 +++++---- client/iface/device/device_kernel_unix.go | 16 +++++++++------- client/iface/device/device_netstack.go | 9 +++++---- client/iface/device/device_usp_unix.go | 9 +++++---- client/iface/device/device_windows.go | 9 +++++---- client/iface/device/wg_link_freebsd.go | 3 ++- client/iface/device/wg_link_linux.go | 4 +++- client/iface/device_android.go | 5 +++-- client/iface/iface.go | 7 +++---- client/iface/iface_new_android.go | 5 +++-- client/iface/iface_new_darwin.go | 5 +++-- client/iface/iface_new_ios.go | 5 +++-- client/iface/iface_new_unix.go | 7 ++++--- client/iface/iface_new_windows.go | 5 +++-- client/iface/{device => wgaddr}/address.go | 14 +++++++------- client/internal/acl/manager_test.go | 6 +++--- client/internal/acl/mocks/iface_mapper.go | 6 +++--- client/internal/dns/server_test.go | 5 +++-- client/internal/dns/wgiface.go | 4 ++-- client/internal/dns/wgiface_windows.go | 4 ++-- client/internal/engine_test.go | 9 +++++---- client/internal/iface_common.go | 3 ++- client/internal/peer/iface.go | 2 ++ client/internal/peer/worker_ice.go | 14 +++++++------- .../routemanager/iface/iface_common.go | 4 ++-- 41 files changed, 180 insertions(+), 144 deletions(-) rename client/iface/{device => wgaddr}/address.go (61%) diff --git a/client/firewall/iface.go b/client/firewall/iface.go index d842abaa1..b83c5f912 100644 --- a/client/firewall/iface.go +++ b/client/firewall/iface.go @@ -4,12 +4,13 @@ import ( wgdevice "golang.zx2c4.com/wireguard/device" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) // IFaceMapper defines subset methods of interface required for manager type IFaceMapper interface { Name() string - Address() device.WGAddress + Address() wgaddr.Address IsUserspaceBind() bool SetFilter(device.PacketFilter) error GetDevice() *device.FilteredDevice diff --git a/client/firewall/iptables/manager_linux.go b/client/firewall/iptables/manager_linux.go index 42450fc28..4b8606834 100644 --- a/client/firewall/iptables/manager_linux.go +++ b/client/firewall/iptables/manager_linux.go @@ -13,7 +13,7 @@ import ( nberrors "github.com/netbirdio/netbird/client/errors" firewall "github.com/netbirdio/netbird/client/firewall/manager" - "github.com/netbirdio/netbird/client/iface" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/internal/statemanager" ) @@ -31,7 +31,7 @@ type Manager struct { // iFaceMapper defines subset methods of interface required for manager type iFaceMapper interface { Name() string - Address() iface.WGAddress + Address() wgaddr.Address IsUserspaceBind() bool } diff --git a/client/firewall/iptables/manager_linux_test.go b/client/firewall/iptables/manager_linux_test.go index 856633409..ad282670b 100644 --- a/client/firewall/iptables/manager_linux_test.go +++ b/client/firewall/iptables/manager_linux_test.go @@ -10,15 +10,15 @@ import ( "github.com/stretchr/testify/require" fw "github.com/netbirdio/netbird/client/firewall/manager" - "github.com/netbirdio/netbird/client/iface" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) var ifaceMock = &iFaceMock{ NameFunc: func() string { return "lo" }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: net.ParseIP("10.20.0.1"), Network: &net.IPNet{ IP: net.ParseIP("10.20.0.0"), @@ -31,7 +31,7 @@ var ifaceMock = &iFaceMock{ // iFaceMapper defines subset methods of interface required for manager type iFaceMock struct { NameFunc func() string - AddressFunc func() iface.WGAddress + AddressFunc func() wgaddr.Address } func (i *iFaceMock) Name() string { @@ -41,7 +41,7 @@ func (i *iFaceMock) Name() string { panic("NameFunc is not set") } -func (i *iFaceMock) Address() iface.WGAddress { +func (i *iFaceMock) Address() wgaddr.Address { if i.AddressFunc != nil { return i.AddressFunc() } @@ -117,8 +117,8 @@ func TestIptablesManagerIPSet(t *testing.T) { NameFunc: func() string { return "lo" }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: net.ParseIP("10.20.0.1"), Network: &net.IPNet{ IP: net.ParseIP("10.20.0.0"), @@ -184,8 +184,8 @@ func TestIptablesCreatePerformance(t *testing.T) { NameFunc: func() string { return "lo" }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: net.ParseIP("10.20.0.1"), Network: &net.IPNet{ IP: net.ParseIP("10.20.0.0"), diff --git a/client/firewall/iptables/state_linux.go b/client/firewall/iptables/state_linux.go index 2a7120bbf..6ef159e01 100644 --- a/client/firewall/iptables/state_linux.go +++ b/client/firewall/iptables/state_linux.go @@ -4,21 +4,20 @@ import ( "fmt" "sync" - "github.com/netbirdio/netbird/client/iface" - "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type InterfaceState struct { - NameStr string `json:"name"` - WGAddress iface.WGAddress `json:"wg_address"` - UserspaceBind bool `json:"userspace_bind"` + NameStr string `json:"name"` + WGAddress wgaddr.Address `json:"wg_address"` + UserspaceBind bool `json:"userspace_bind"` } func (i *InterfaceState) Name() string { return i.NameStr } -func (i *InterfaceState) Address() device.WGAddress { +func (i *InterfaceState) Address() wgaddr.Address { return i.WGAddress } diff --git a/client/firewall/nftables/manager_linux.go b/client/firewall/nftables/manager_linux.go index 6a8e39735..3cdd12c72 100644 --- a/client/firewall/nftables/manager_linux.go +++ b/client/firewall/nftables/manager_linux.go @@ -14,7 +14,7 @@ import ( log "github.com/sirupsen/logrus" firewall "github.com/netbirdio/netbird/client/firewall/manager" - "github.com/netbirdio/netbird/client/iface" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/internal/statemanager" ) @@ -29,7 +29,7 @@ const ( // iFaceMapper defines subset methods of interface required for manager type iFaceMapper interface { Name() string - Address() iface.WGAddress + Address() wgaddr.Address IsUserspaceBind() bool } diff --git a/client/firewall/nftables/manager_linux_test.go b/client/firewall/nftables/manager_linux_test.go index 9ca20889b..c4b13045e 100644 --- a/client/firewall/nftables/manager_linux_test.go +++ b/client/firewall/nftables/manager_linux_test.go @@ -16,15 +16,15 @@ import ( "golang.org/x/sys/unix" fw "github.com/netbirdio/netbird/client/firewall/manager" - "github.com/netbirdio/netbird/client/iface" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) var ifaceMock = &iFaceMock{ NameFunc: func() string { return "lo" }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: net.ParseIP("100.96.0.1"), Network: &net.IPNet{ IP: net.ParseIP("100.96.0.0"), @@ -37,7 +37,7 @@ var ifaceMock = &iFaceMock{ // iFaceMapper defines subset methods of interface required for manager type iFaceMock struct { NameFunc func() string - AddressFunc func() iface.WGAddress + AddressFunc func() wgaddr.Address } func (i *iFaceMock) Name() string { @@ -47,7 +47,7 @@ func (i *iFaceMock) Name() string { panic("NameFunc is not set") } -func (i *iFaceMock) Address() iface.WGAddress { +func (i *iFaceMock) Address() wgaddr.Address { if i.AddressFunc != nil { return i.AddressFunc() } @@ -171,8 +171,8 @@ func TestNFtablesCreatePerformance(t *testing.T) { NameFunc: func() string { return "lo" }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: net.ParseIP("100.96.0.1"), Network: &net.IPNet{ IP: net.ParseIP("100.96.0.0"), diff --git a/client/firewall/nftables/state_linux.go b/client/firewall/nftables/state_linux.go index facca1cec..f805623d6 100644 --- a/client/firewall/nftables/state_linux.go +++ b/client/firewall/nftables/state_linux.go @@ -3,21 +3,20 @@ package nftables import ( "fmt" - "github.com/netbirdio/netbird/client/iface" - "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type InterfaceState struct { - NameStr string `json:"name"` - WGAddress iface.WGAddress `json:"wg_address"` - UserspaceBind bool `json:"userspace_bind"` + NameStr string `json:"name"` + WGAddress wgaddr.Address `json:"wg_address"` + UserspaceBind bool `json:"userspace_bind"` } func (i *InterfaceState) Name() string { return i.NameStr } -func (i *InterfaceState) Address() device.WGAddress { +func (i *InterfaceState) Address() wgaddr.Address { return i.WGAddress } diff --git a/client/firewall/uspfilter/common/iface.go b/client/firewall/uspfilter/common/iface.go index d44e79509..7296953db 100644 --- a/client/firewall/uspfilter/common/iface.go +++ b/client/firewall/uspfilter/common/iface.go @@ -3,14 +3,14 @@ package common import ( wgdevice "golang.zx2c4.com/wireguard/device" - "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) // IFaceMapper defines subset methods of interface required for manager type IFaceMapper interface { SetFilter(device.PacketFilter) error - Address() iface.WGAddress + Address() wgaddr.Address GetWGDevice() *wgdevice.Device GetDevice() *device.FilteredDevice } diff --git a/client/firewall/uspfilter/localip_test.go b/client/firewall/uspfilter/localip_test.go index 02f41bf4f..0a2a7b355 100644 --- a/client/firewall/uspfilter/localip_test.go +++ b/client/firewall/uspfilter/localip_test.go @@ -6,19 +6,19 @@ import ( "github.com/stretchr/testify/require" - "github.com/netbirdio/netbird/client/iface" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) func TestLocalIPManager(t *testing.T) { tests := []struct { name string - setupAddr iface.WGAddress + setupAddr wgaddr.Address testIP net.IP expected bool }{ { name: "Localhost range", - setupAddr: iface.WGAddress{ + setupAddr: wgaddr.Address{ IP: net.ParseIP("192.168.1.1"), Network: &net.IPNet{ IP: net.ParseIP("192.168.1.0"), @@ -30,7 +30,7 @@ func TestLocalIPManager(t *testing.T) { }, { name: "Localhost standard address", - setupAddr: iface.WGAddress{ + setupAddr: wgaddr.Address{ IP: net.ParseIP("192.168.1.1"), Network: &net.IPNet{ IP: net.ParseIP("192.168.1.0"), @@ -42,7 +42,7 @@ func TestLocalIPManager(t *testing.T) { }, { name: "Localhost range edge", - setupAddr: iface.WGAddress{ + setupAddr: wgaddr.Address{ IP: net.ParseIP("192.168.1.1"), Network: &net.IPNet{ IP: net.ParseIP("192.168.1.0"), @@ -54,7 +54,7 @@ func TestLocalIPManager(t *testing.T) { }, { name: "Local IP matches", - setupAddr: iface.WGAddress{ + setupAddr: wgaddr.Address{ IP: net.ParseIP("192.168.1.1"), Network: &net.IPNet{ IP: net.ParseIP("192.168.1.0"), @@ -66,7 +66,7 @@ func TestLocalIPManager(t *testing.T) { }, { name: "Local IP doesn't match", - setupAddr: iface.WGAddress{ + setupAddr: wgaddr.Address{ IP: net.ParseIP("192.168.1.1"), Network: &net.IPNet{ IP: net.ParseIP("192.168.1.0"), @@ -78,7 +78,7 @@ func TestLocalIPManager(t *testing.T) { }, { name: "IPv6 address", - setupAddr: iface.WGAddress{ + setupAddr: wgaddr.Address{ IP: net.ParseIP("fe80::1"), Network: &net.IPNet{ IP: net.ParseIP("fe80::"), @@ -95,7 +95,7 @@ func TestLocalIPManager(t *testing.T) { manager := newLocalIPManager() mock := &IFaceMock{ - AddressFunc: func() iface.WGAddress { + AddressFunc: func() wgaddr.Address { return tt.setupAddr }, } diff --git a/client/firewall/uspfilter/uspfilter_filter_test.go b/client/firewall/uspfilter/uspfilter_filter_test.go index 9a5ec9c66..1497a5ba7 100644 --- a/client/firewall/uspfilter/uspfilter_filter_test.go +++ b/client/firewall/uspfilter/uspfilter_filter_test.go @@ -12,9 +12,9 @@ import ( wgdevice "golang.zx2c4.com/wireguard/device" fw "github.com/netbirdio/netbird/client/firewall/manager" - "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/mocks" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) func TestPeerACLFiltering(t *testing.T) { @@ -26,8 +26,8 @@ func TestPeerACLFiltering(t *testing.T) { ifaceMock := &IFaceMock{ SetFilterFunc: func(device.PacketFilter) error { return nil }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: localIP, Network: wgNet, } @@ -288,8 +288,8 @@ func setupRoutedManager(tb testing.TB, network string) *Manager { ifaceMock := &IFaceMock{ SetFilterFunc: func(device.PacketFilter) error { return nil }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: localIP, Network: wgNet, } diff --git a/client/firewall/uspfilter/uspfilter_test.go b/client/firewall/uspfilter/uspfilter_test.go index f197f4f90..bcb9624ee 100644 --- a/client/firewall/uspfilter/uspfilter_test.go +++ b/client/firewall/uspfilter/uspfilter_test.go @@ -16,15 +16,15 @@ import ( fw "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack" "github.com/netbirdio/netbird/client/firewall/uspfilter/log" - "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) var logger = log.NewFromLogrus(logrus.StandardLogger()) type IFaceMock struct { SetFilterFunc func(device.PacketFilter) error - AddressFunc func() iface.WGAddress + AddressFunc func() wgaddr.Address GetWGDeviceFunc func() *wgdevice.Device GetDeviceFunc func() *device.FilteredDevice } @@ -50,9 +50,9 @@ func (i *IFaceMock) SetFilter(iface device.PacketFilter) error { return i.SetFilterFunc(iface) } -func (i *IFaceMock) Address() iface.WGAddress { +func (i *IFaceMock) Address() wgaddr.Address { if i.AddressFunc == nil { - return iface.WGAddress{} + return wgaddr.Address{} } return i.AddressFunc() } @@ -268,8 +268,8 @@ func TestManagerReset(t *testing.T) { func TestNotMatchByIP(t *testing.T) { ifaceMock := &IFaceMock{ SetFilterFunc: func(device.PacketFilter) error { return nil }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: net.ParseIP("100.10.0.100"), Network: &net.IPNet{ IP: net.ParseIP("100.10.0.0"), diff --git a/client/iface/bind/ice_bind.go b/client/iface/bind/ice_bind.go index 6897f04a1..66ec6a00d 100644 --- a/client/iface/bind/ice_bind.go +++ b/client/iface/bind/ice_bind.go @@ -13,6 +13,8 @@ import ( "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" wgConn "golang.zx2c4.com/wireguard/conn" + + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type RecvMessage struct { @@ -51,9 +53,10 @@ type ICEBind struct { muUDPMux sync.Mutex udpMux *UniversalUDPMuxDefault + address wgaddr.Address } -func NewICEBind(transportNet transport.Net, filterFn FilterFn) *ICEBind { +func NewICEBind(transportNet transport.Net, filterFn FilterFn, address wgaddr.Address) *ICEBind { b, _ := wgConn.NewStdNetBind().(*wgConn.StdNetBind) ib := &ICEBind{ StdNetBind: b, @@ -63,6 +66,7 @@ func NewICEBind(transportNet transport.Net, filterFn FilterFn) *ICEBind { endpoints: make(map[netip.Addr]net.Conn), closedChan: make(chan struct{}), closed: true, + address: address, } rc := receiverCreator{ @@ -142,9 +146,10 @@ func (s *ICEBind) createIPv4ReceiverFn(pc *ipv4.PacketConn, conn *net.UDPConn, r s.udpMux = NewUniversalUDPMuxDefault( UniversalUDPMuxParams{ - UDPConn: conn, - Net: s.transportNet, - FilterFn: s.filterFn, + UDPConn: conn, + Net: s.transportNet, + FilterFn: s.filterFn, + WGAddress: s.address, }, ) return func(bufs [][]byte, sizes []int, eps []wgConn.Endpoint) (n int, err error) { diff --git a/client/iface/bind/udp_mux_universal.go b/client/iface/bind/udp_mux_universal.go index ebbefe035..6f851393e 100644 --- a/client/iface/bind/udp_mux_universal.go +++ b/client/iface/bind/udp_mux_universal.go @@ -17,6 +17,8 @@ import ( "github.com/pion/logging" "github.com/pion/stun/v2" "github.com/pion/transport/v3" + + "github.com/netbirdio/netbird/client/iface/wgaddr" ) // FilterFn is a function that filters out candidates based on the address. @@ -41,6 +43,7 @@ type UniversalUDPMuxParams struct { XORMappedAddrCacheTTL time.Duration Net transport.Net FilterFn FilterFn + WGAddress wgaddr.Address } // NewUniversalUDPMuxDefault creates an implementation of UniversalUDPMux embedding UDPMux @@ -64,6 +67,7 @@ func NewUniversalUDPMuxDefault(params UniversalUDPMuxParams) *UniversalUDPMuxDef mux: m, logger: params.Logger, filterFn: params.FilterFn, + address: params.WGAddress, } // embed UDPMux @@ -118,6 +122,7 @@ type udpConn struct { filterFn FilterFn // TODO: reset cache on route changes addrCache sync.Map + address wgaddr.Address } func (u *udpConn) WriteTo(b []byte, addr net.Addr) (int, error) { @@ -159,6 +164,11 @@ func (u *udpConn) performFilterCheck(addr net.Addr) error { return nil } + if u.address.Network.Contains(a.AsSlice()) { + log.Warnf("Address %s is part of the NetBird network %s, refusing to write", addr, u.address) + return fmt.Errorf("address %s is part of the NetBird network %s, refusing to write", addr, u.address) + } + if isRouted, prefix, err := u.filterFn(a); err != nil { log.Errorf("Failed to check if address %s is routed: %v", addr, err) } else { diff --git a/client/iface/device.go b/client/iface/device.go index 86e9dab4b..81f2e0f47 100644 --- a/client/iface/device.go +++ b/client/iface/device.go @@ -9,13 +9,14 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type WGTunDevice interface { Create() (device.WGConfigurer, error) Up() (*bind.UniversalUDPMuxDefault, error) - UpdateAddr(address WGAddress) error - WgAddress() WGAddress + UpdateAddr(address wgaddr.Address) error + WgAddress() wgaddr.Address DeviceName() string Close() error FilteredDevice() *device.FilteredDevice diff --git a/client/iface/device/device_android.go b/client/iface/device/device_android.go index 55081e181..ab3e611e1 100644 --- a/client/iface/device/device_android.go +++ b/client/iface/device/device_android.go @@ -13,11 +13,12 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) // WGTunDevice ignore the WGTunDevice interface on Android because the creation of the tun device is different on this platform type WGTunDevice struct { - address WGAddress + address wgaddr.Address port int key string mtu int @@ -31,7 +32,7 @@ type WGTunDevice struct { configurer WGConfigurer } -func NewTunDevice(address WGAddress, port int, key string, mtu int, iceBind *bind.ICEBind, tunAdapter TunAdapter) *WGTunDevice { +func NewTunDevice(address wgaddr.Address, port int, key string, mtu int, iceBind *bind.ICEBind, tunAdapter TunAdapter) *WGTunDevice { return &WGTunDevice{ address: address, port: port, @@ -93,7 +94,7 @@ func (t *WGTunDevice) Up() (*bind.UniversalUDPMuxDefault, error) { return udpMux, nil } -func (t *WGTunDevice) UpdateAddr(addr WGAddress) error { +func (t *WGTunDevice) UpdateAddr(addr wgaddr.Address) error { // todo implement return nil } @@ -123,7 +124,7 @@ func (t *WGTunDevice) DeviceName() string { return t.name } -func (t *WGTunDevice) WgAddress() WGAddress { +func (t *WGTunDevice) WgAddress() wgaddr.Address { return t.address } diff --git a/client/iface/device/device_darwin.go b/client/iface/device/device_darwin.go index 1a5635ff2..01bfbf381 100644 --- a/client/iface/device/device_darwin.go +++ b/client/iface/device/device_darwin.go @@ -13,11 +13,12 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type TunDevice struct { name string - address WGAddress + address wgaddr.Address port int key string mtu int @@ -29,7 +30,7 @@ type TunDevice struct { configurer WGConfigurer } -func NewTunDevice(name string, address WGAddress, port int, key string, mtu int, iceBind *bind.ICEBind) *TunDevice { +func NewTunDevice(name string, address wgaddr.Address, port int, key string, mtu int, iceBind *bind.ICEBind) *TunDevice { return &TunDevice{ name: name, address: address, @@ -85,7 +86,7 @@ func (t *TunDevice) Up() (*bind.UniversalUDPMuxDefault, error) { return udpMux, nil } -func (t *TunDevice) UpdateAddr(address WGAddress) error { +func (t *TunDevice) UpdateAddr(address wgaddr.Address) error { t.address = address return t.assignAddr() } @@ -106,7 +107,7 @@ func (t *TunDevice) Close() error { return nil } -func (t *TunDevice) WgAddress() WGAddress { +func (t *TunDevice) WgAddress() wgaddr.Address { return t.address } diff --git a/client/iface/device/device_ios.go b/client/iface/device/device_ios.go index b106d475c..56d44d68e 100644 --- a/client/iface/device/device_ios.go +++ b/client/iface/device/device_ios.go @@ -14,11 +14,12 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type TunDevice struct { name string - address WGAddress + address wgaddr.Address port int key string iceBind *bind.ICEBind @@ -30,7 +31,7 @@ type TunDevice struct { configurer WGConfigurer } -func NewTunDevice(name string, address WGAddress, port int, key string, iceBind *bind.ICEBind, tunFd int) *TunDevice { +func NewTunDevice(name string, address wgaddr.Address, port int, key string, iceBind *bind.ICEBind, tunFd int) *TunDevice { return &TunDevice{ name: name, address: address, @@ -120,11 +121,11 @@ func (t *TunDevice) Close() error { return nil } -func (t *TunDevice) WgAddress() WGAddress { +func (t *TunDevice) WgAddress() wgaddr.Address { return t.address } -func (t *TunDevice) UpdateAddr(addr WGAddress) error { +func (t *TunDevice) UpdateAddr(_ wgaddr.Address) error { // todo implement return nil } diff --git a/client/iface/device/device_kernel_unix.go b/client/iface/device/device_kernel_unix.go index fe1d1147f..988ed1b39 100644 --- a/client/iface/device/device_kernel_unix.go +++ b/client/iface/device/device_kernel_unix.go @@ -14,12 +14,13 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/sharedsock" ) type TunKernelDevice struct { name string - address WGAddress + address wgaddr.Address wgPort int key string mtu int @@ -34,7 +35,7 @@ type TunKernelDevice struct { filterFn bind.FilterFn } -func NewKernelDevice(name string, address WGAddress, wgPort int, key string, mtu int, transportNet transport.Net) *TunKernelDevice { +func NewKernelDevice(name string, address wgaddr.Address, wgPort int, key string, mtu int, transportNet transport.Net) *TunKernelDevice { ctx, cancel := context.WithCancel(context.Background()) return &TunKernelDevice{ ctx: ctx, @@ -99,9 +100,10 @@ func (t *TunKernelDevice) Up() (*bind.UniversalUDPMuxDefault, error) { return nil, err } bindParams := bind.UniversalUDPMuxParams{ - UDPConn: rawSock, - Net: t.transportNet, - FilterFn: t.filterFn, + UDPConn: rawSock, + Net: t.transportNet, + FilterFn: t.filterFn, + WGAddress: t.address, } mux := bind.NewUniversalUDPMuxDefault(bindParams) go mux.ReadFromConn(t.ctx) @@ -112,7 +114,7 @@ func (t *TunKernelDevice) Up() (*bind.UniversalUDPMuxDefault, error) { return t.udpMux, nil } -func (t *TunKernelDevice) UpdateAddr(address WGAddress) error { +func (t *TunKernelDevice) UpdateAddr(address wgaddr.Address) error { t.address = address return t.assignAddr() } @@ -145,7 +147,7 @@ func (t *TunKernelDevice) Close() error { return closErr } -func (t *TunKernelDevice) WgAddress() WGAddress { +func (t *TunKernelDevice) WgAddress() wgaddr.Address { return t.address } diff --git a/client/iface/device/device_netstack.go b/client/iface/device/device_netstack.go index 0cb02fd19..d3c92235e 100644 --- a/client/iface/device/device_netstack.go +++ b/client/iface/device/device_netstack.go @@ -13,12 +13,13 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" nbnetstack "github.com/netbirdio/netbird/client/iface/netstack" + "github.com/netbirdio/netbird/client/iface/wgaddr" nbnet "github.com/netbirdio/netbird/util/net" ) type TunNetstackDevice struct { name string - address WGAddress + address wgaddr.Address port int key string mtu int @@ -34,7 +35,7 @@ type TunNetstackDevice struct { net *netstack.Net } -func NewNetstackDevice(name string, address WGAddress, wgPort int, key string, mtu int, iceBind *bind.ICEBind, listenAddress string) *TunNetstackDevice { +func NewNetstackDevice(name string, address wgaddr.Address, wgPort int, key string, mtu int, iceBind *bind.ICEBind, listenAddress string) *TunNetstackDevice { return &TunNetstackDevice{ name: name, address: address, @@ -97,7 +98,7 @@ func (t *TunNetstackDevice) Up() (*bind.UniversalUDPMuxDefault, error) { return udpMux, nil } -func (t *TunNetstackDevice) UpdateAddr(WGAddress) error { +func (t *TunNetstackDevice) UpdateAddr(wgaddr.Address) error { return nil } @@ -116,7 +117,7 @@ func (t *TunNetstackDevice) Close() error { return nil } -func (t *TunNetstackDevice) WgAddress() WGAddress { +func (t *TunNetstackDevice) WgAddress() wgaddr.Address { return t.address } diff --git a/client/iface/device/device_usp_unix.go b/client/iface/device/device_usp_unix.go index 07570617a..c45ae9676 100644 --- a/client/iface/device/device_usp_unix.go +++ b/client/iface/device/device_usp_unix.go @@ -12,11 +12,12 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type USPDevice struct { name string - address WGAddress + address wgaddr.Address port int key string mtu int @@ -28,7 +29,7 @@ type USPDevice struct { configurer WGConfigurer } -func NewUSPDevice(name string, address WGAddress, port int, key string, mtu int, iceBind *bind.ICEBind) *USPDevice { +func NewUSPDevice(name string, address wgaddr.Address, port int, key string, mtu int, iceBind *bind.ICEBind) *USPDevice { log.Infof("using userspace bind mode") return &USPDevice{ @@ -93,7 +94,7 @@ func (t *USPDevice) Up() (*bind.UniversalUDPMuxDefault, error) { return udpMux, nil } -func (t *USPDevice) UpdateAddr(address WGAddress) error { +func (t *USPDevice) UpdateAddr(address wgaddr.Address) error { t.address = address return t.assignAddr() } @@ -113,7 +114,7 @@ func (t *USPDevice) Close() error { return nil } -func (t *USPDevice) WgAddress() WGAddress { +func (t *USPDevice) WgAddress() wgaddr.Address { return t.address } diff --git a/client/iface/device/device_windows.go b/client/iface/device/device_windows.go index 0fd1b3326..41e615bc2 100644 --- a/client/iface/device/device_windows.go +++ b/client/iface/device/device_windows.go @@ -13,13 +13,14 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) const defaultWindowsGUIDSTring = "{f2f29e61-d91f-4d76-8151-119b20c4bdeb}" type TunDevice struct { name string - address WGAddress + address wgaddr.Address port int key string mtu int @@ -32,7 +33,7 @@ type TunDevice struct { configurer WGConfigurer } -func NewTunDevice(name string, address WGAddress, port int, key string, mtu int, iceBind *bind.ICEBind) *TunDevice { +func NewTunDevice(name string, address wgaddr.Address, port int, key string, mtu int, iceBind *bind.ICEBind) *TunDevice { return &TunDevice{ name: name, address: address, @@ -118,7 +119,7 @@ func (t *TunDevice) Up() (*bind.UniversalUDPMuxDefault, error) { return udpMux, nil } -func (t *TunDevice) UpdateAddr(address WGAddress) error { +func (t *TunDevice) UpdateAddr(address wgaddr.Address) error { t.address = address return t.assignAddr() } @@ -139,7 +140,7 @@ func (t *TunDevice) Close() error { } return nil } -func (t *TunDevice) WgAddress() WGAddress { +func (t *TunDevice) WgAddress() wgaddr.Address { return t.address } diff --git a/client/iface/device/wg_link_freebsd.go b/client/iface/device/wg_link_freebsd.go index 104010f47..9067790e4 100644 --- a/client/iface/device/wg_link_freebsd.go +++ b/client/iface/device/wg_link_freebsd.go @@ -6,6 +6,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/netbirdio/netbird/client/iface/freebsd" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type wgLink struct { @@ -56,7 +57,7 @@ func (l *wgLink) up() error { return nil } -func (l *wgLink) assignAddr(address WGAddress) error { +func (l *wgLink) assignAddr(address wgaddr.Address) error { link, err := freebsd.LinkByName(l.name) if err != nil { return fmt.Errorf("link by name: %w", err) diff --git a/client/iface/device/wg_link_linux.go b/client/iface/device/wg_link_linux.go index a15cffe48..d941cd022 100644 --- a/client/iface/device/wg_link_linux.go +++ b/client/iface/device/wg_link_linux.go @@ -8,6 +8,8 @@ import ( log "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" + + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type wgLink struct { @@ -90,7 +92,7 @@ func (l *wgLink) up() error { return nil } -func (l *wgLink) assignAddr(address WGAddress) error { +func (l *wgLink) assignAddr(address wgaddr.Address) error { //delete existing addresses list, err := netlink.AddrList(l, 0) if err != nil { diff --git a/client/iface/device_android.go b/client/iface/device_android.go index 5cbeb70f8..a1e246fc5 100644 --- a/client/iface/device_android.go +++ b/client/iface/device_android.go @@ -7,13 +7,14 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type WGTunDevice interface { Create(routes []string, dns string, searchDomains []string) (device.WGConfigurer, error) Up() (*bind.UniversalUDPMuxDefault, error) - UpdateAddr(address WGAddress) error - WgAddress() WGAddress + UpdateAddr(address wgaddr.Address) error + WgAddress() wgaddr.Address DeviceName() string Close() error FilteredDevice() *device.FilteredDevice diff --git a/client/iface/iface.go b/client/iface/iface.go index 40bd51fbb..9d5262aed 100644 --- a/client/iface/iface.go +++ b/client/iface/iface.go @@ -19,6 +19,7 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" ) @@ -28,8 +29,6 @@ const ( WgInterfaceDefault = configurer.WgInterfaceDefault ) -type WGAddress = device.WGAddress - type wgProxyFactory interface { GetProxy() wgproxy.Proxy Free() error @@ -72,7 +71,7 @@ func (w *WGIface) Name() string { } // Address returns the interface address -func (w *WGIface) Address() device.WGAddress { +func (w *WGIface) Address() wgaddr.Address { return w.tun.WgAddress() } @@ -103,7 +102,7 @@ func (w *WGIface) UpdateAddr(newAddr string) error { w.mu.Lock() defer w.mu.Unlock() - addr, err := device.ParseWGAddress(newAddr) + addr, err := wgaddr.ParseWGAddress(newAddr) if err != nil { return err } diff --git a/client/iface/iface_new_android.go b/client/iface/iface_new_android.go index 69a8d1fd4..35046b887 100644 --- a/client/iface/iface_new_android.go +++ b/client/iface/iface_new_android.go @@ -3,17 +3,18 @@ package iface import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" ) // NewWGIFace Creates a new WireGuard interface instance func NewWGIFace(opts WGIFaceOpts) (*WGIface, error) { - wgAddress, err := device.ParseWGAddress(opts.Address) + wgAddress, err := wgaddr.ParseWGAddress(opts.Address) if err != nil { return nil, err } - iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn) + iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn, wgAddress) wgIFace := &WGIface{ userspaceBind: true, diff --git a/client/iface/iface_new_darwin.go b/client/iface/iface_new_darwin.go index a92d74e0f..93fd7fd5c 100644 --- a/client/iface/iface_new_darwin.go +++ b/client/iface/iface_new_darwin.go @@ -6,17 +6,18 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/netstack" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" ) // NewWGIFace Creates a new WireGuard interface instance func NewWGIFace(opts WGIFaceOpts) (*WGIface, error) { - wgAddress, err := device.ParseWGAddress(opts.Address) + wgAddress, err := wgaddr.ParseWGAddress(opts.Address) if err != nil { return nil, err } - iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn) + iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn, wgAddress) var tun WGTunDevice if netstack.IsEnabled() { diff --git a/client/iface/iface_new_ios.go b/client/iface/iface_new_ios.go index 363f95e11..317ee0f46 100644 --- a/client/iface/iface_new_ios.go +++ b/client/iface/iface_new_ios.go @@ -5,17 +5,18 @@ package iface import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" ) // NewWGIFace Creates a new WireGuard interface instance func NewWGIFace(opts WGIFaceOpts) (*WGIface, error) { - wgAddress, err := device.ParseWGAddress(opts.Address) + wgAddress, err := wgaddr.ParseWGAddress(opts.Address) if err != nil { return nil, err } - iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn) + iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn, wgAddress) wgIFace := &WGIface{ tun: device.NewTunDevice(opts.IFaceName, wgAddress, opts.WGPort, opts.WGPrivKey, iceBind, opts.MobileArgs.TunFd), diff --git a/client/iface/iface_new_unix.go b/client/iface/iface_new_unix.go index f10b17c9a..23ee7236f 100644 --- a/client/iface/iface_new_unix.go +++ b/client/iface/iface_new_unix.go @@ -8,12 +8,13 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/netstack" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" ) // NewWGIFace Creates a new WireGuard interface instance func NewWGIFace(opts WGIFaceOpts) (*WGIface, error) { - wgAddress, err := device.ParseWGAddress(opts.Address) + wgAddress, err := wgaddr.ParseWGAddress(opts.Address) if err != nil { return nil, err } @@ -21,7 +22,7 @@ func NewWGIFace(opts WGIFaceOpts) (*WGIface, error) { wgIFace := &WGIface{} if netstack.IsEnabled() { - iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn) + iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn, wgAddress) wgIFace.tun = device.NewNetstackDevice(opts.IFaceName, wgAddress, opts.WGPort, opts.WGPrivKey, opts.MTU, iceBind, netstack.ListenAddr()) wgIFace.userspaceBind = true wgIFace.wgProxyFactory = wgproxy.NewUSPFactory(iceBind) @@ -34,7 +35,7 @@ func NewWGIFace(opts WGIFaceOpts) (*WGIface, error) { return wgIFace, nil } if device.ModuleTunIsLoaded() { - iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn) + iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn, wgAddress) wgIFace.tun = device.NewUSPDevice(opts.IFaceName, wgAddress, opts.WGPort, opts.WGPrivKey, opts.MTU, iceBind) wgIFace.userspaceBind = true wgIFace.wgProxyFactory = wgproxy.NewUSPFactory(iceBind) diff --git a/client/iface/iface_new_windows.go b/client/iface/iface_new_windows.go index 2e6355496..413062940 100644 --- a/client/iface/iface_new_windows.go +++ b/client/iface/iface_new_windows.go @@ -4,16 +4,17 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/netstack" + wgaddr "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" ) // NewWGIFace Creates a new WireGuard interface instance func NewWGIFace(opts WGIFaceOpts) (*WGIface, error) { - wgAddress, err := device.ParseWGAddress(opts.Address) + wgAddress, err := wgaddr.ParseWGAddress(opts.Address) if err != nil { return nil, err } - iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn) + iceBind := bind.NewICEBind(opts.TransportNet, opts.FilterFn, wgAddress) var tun WGTunDevice if netstack.IsEnabled() { diff --git a/client/iface/device/address.go b/client/iface/wgaddr/address.go similarity index 61% rename from client/iface/device/address.go rename to client/iface/wgaddr/address.go index 15de301da..e5079258c 100644 --- a/client/iface/device/address.go +++ b/client/iface/wgaddr/address.go @@ -1,29 +1,29 @@ -package device +package wgaddr import ( "fmt" "net" ) -// WGAddress WireGuard parsed address -type WGAddress struct { +// Address WireGuard parsed address +type Address struct { IP net.IP Network *net.IPNet } // ParseWGAddress parse a string ("1.2.3.4/24") address to WG Address -func ParseWGAddress(address string) (WGAddress, error) { +func ParseWGAddress(address string) (Address, error) { ip, network, err := net.ParseCIDR(address) if err != nil { - return WGAddress{}, err + return Address{}, err } - return WGAddress{ + return Address{ IP: ip, Network: network, }, nil } -func (addr WGAddress) String() string { +func (addr Address) String() string { maskSize, _ := addr.Network.Mask.Size() return fmt.Sprintf("%s/%d", addr.IP.String(), maskSize) } diff --git a/client/internal/acl/manager_test.go b/client/internal/acl/manager_test.go index f6dc749f6..e054d69e9 100644 --- a/client/internal/acl/manager_test.go +++ b/client/internal/acl/manager_test.go @@ -8,7 +8,7 @@ import ( "github.com/netbirdio/netbird/client/firewall" "github.com/netbirdio/netbird/client/firewall/manager" - "github.com/netbirdio/netbird/client/iface" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/internal/acl/mocks" mgmProto "github.com/netbirdio/netbird/management/proto" ) @@ -45,7 +45,7 @@ func TestDefaultManager(t *testing.T) { } ifaceMock.EXPECT().Name().Return("lo").AnyTimes() - ifaceMock.EXPECT().Address().Return(iface.WGAddress{ + ifaceMock.EXPECT().Address().Return(wgaddr.Address{ IP: ip, Network: network, }).AnyTimes() @@ -339,7 +339,7 @@ func TestDefaultManagerEnableSSHRules(t *testing.T) { } ifaceMock.EXPECT().Name().Return("lo").AnyTimes() - ifaceMock.EXPECT().Address().Return(iface.WGAddress{ + ifaceMock.EXPECT().Address().Return(wgaddr.Address{ IP: ip, Network: network, }).AnyTimes() diff --git a/client/internal/acl/mocks/iface_mapper.go b/client/internal/acl/mocks/iface_mapper.go index 08aa4fd5a..95d5a2c58 100644 --- a/client/internal/acl/mocks/iface_mapper.go +++ b/client/internal/acl/mocks/iface_mapper.go @@ -10,8 +10,8 @@ import ( gomock "github.com/golang/mock/gomock" wgdevice "golang.zx2c4.com/wireguard/device" - iface "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) // MockIFaceMapper is a mock of IFaceMapper interface. @@ -38,10 +38,10 @@ func (m *MockIFaceMapper) EXPECT() *MockIFaceMapperMockRecorder { } // Address mocks base method. -func (m *MockIFaceMapper) Address() iface.WGAddress { +func (m *MockIFaceMapper) Address() wgaddr.Address { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Address") - ret0, _ := ret[0].(iface.WGAddress) + ret0, _ := ret[0].(wgaddr.Address) return ret0 } diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index d9886fcd8..a14942c34 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -22,6 +22,7 @@ import ( "github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/device" pfmock "github.com/netbirdio/netbird/client/iface/mocks" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/statemanager" "github.com/netbirdio/netbird/client/internal/stdnet" @@ -37,9 +38,9 @@ func (w *mocWGIface) Name() string { panic("implement me") } -func (w *mocWGIface) Address() iface.WGAddress { +func (w *mocWGIface) Address() wgaddr.Address { ip, network, _ := net.ParseCIDR("100.66.100.0/24") - return iface.WGAddress{ + return wgaddr.Address{ IP: ip, Network: network, } diff --git a/client/internal/dns/wgiface.go b/client/internal/dns/wgiface.go index 69bc83659..c6c1752e5 100644 --- a/client/internal/dns/wgiface.go +++ b/client/internal/dns/wgiface.go @@ -5,15 +5,15 @@ package dns import ( "net" - "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) // WGIface defines subset methods of interface required for manager type WGIface interface { Name() string - Address() iface.WGAddress + Address() wgaddr.Address ToInterface() *net.Interface IsUserspaceBind() bool GetFilter() device.PacketFilter diff --git a/client/internal/dns/wgiface_windows.go b/client/internal/dns/wgiface_windows.go index 765132fdb..74e5c75a5 100644 --- a/client/internal/dns/wgiface_windows.go +++ b/client/internal/dns/wgiface_windows.go @@ -1,15 +1,15 @@ package dns import ( - "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) // WGIface defines subset methods of interface required for manager type WGIface interface { Name() string - Address() iface.WGAddress + Address() wgaddr.Address IsUserspaceBind() bool GetFilter() device.PacketFilter GetDevice() *device.FilteredDevice diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 9de1da28d..164880e2f 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -31,6 +31,7 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" "github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/peer" @@ -75,7 +76,7 @@ type MockWGIface struct { CreateOnAndroidFunc func(routeRange []string, ip string, domains []string) error IsUserspaceBindFunc func() bool NameFunc func() string - AddressFunc func() device.WGAddress + AddressFunc func() wgaddr.Address ToInterfaceFunc func() *net.Interface UpFunc func() (*bind.UniversalUDPMuxDefault, error) UpdateAddrFunc func(newAddr string) error @@ -114,7 +115,7 @@ func (m *MockWGIface) Name() string { return m.NameFunc() } -func (m *MockWGIface) Address() device.WGAddress { +func (m *MockWGIface) Address() wgaddr.Address { return m.AddressFunc() } @@ -364,8 +365,8 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { RemovePeerFunc: func(peerKey string) error { return nil }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: net.ParseIP("10.20.0.1"), Network: &net.IPNet{ IP: net.ParseIP("10.20.0.0"), diff --git a/client/internal/iface_common.go b/client/internal/iface_common.go index 65b425015..ffeffaf41 100644 --- a/client/internal/iface_common.go +++ b/client/internal/iface_common.go @@ -12,6 +12,7 @@ import ( "github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" ) @@ -20,7 +21,7 @@ type wgIfaceBase interface { CreateOnAndroid(routeRange []string, ip string, domains []string) error IsUserspaceBind() bool Name() string - Address() device.WGAddress + Address() wgaddr.Address ToInterface() *net.Interface Up() (*bind.UniversalUDPMuxDefault, error) UpdateAddr(newAddr string) error diff --git a/client/internal/peer/iface.go b/client/internal/peer/iface.go index c7b6de9ea..32ac5c7db 100644 --- a/client/internal/peer/iface.go +++ b/client/internal/peer/iface.go @@ -8,6 +8,7 @@ import ( "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/netbirdio/netbird/client/iface/configurer" + "github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgproxy" ) @@ -16,4 +17,5 @@ type WGIface interface { RemovePeer(peerKey string) error GetStats(peerKey string) (configurer.WGStats, error) GetProxy() wgproxy.Proxy + Address() wgaddr.Address } diff --git a/client/internal/peer/worker_ice.go b/client/internal/peer/worker_ice.go index 7dd84a98e..5ceb3f453 100644 --- a/client/internal/peer/worker_ice.go +++ b/client/internal/peer/worker_ice.go @@ -358,6 +358,12 @@ func extraSrflxCandidate(candidate ice.Candidate) (*ice.CandidateServerReflexive } func candidateViaRoutes(candidate ice.Candidate, clientRoutes route.HAMap) bool { + addr, err := netip.ParseAddr(candidate.Address()) + if err != nil { + log.Errorf("Failed to parse IP address %s: %v", candidate.Address(), err) + return false + } + var routePrefixes []netip.Prefix for _, routes := range clientRoutes { if len(routes) > 0 && routes[0] != nil { @@ -365,14 +371,8 @@ func candidateViaRoutes(candidate ice.Candidate, clientRoutes route.HAMap) bool } } - addr, err := netip.ParseAddr(candidate.Address()) - if err != nil { - log.Errorf("Failed to parse IP address %s: %v", candidate.Address(), err) - return false - } - for _, prefix := range routePrefixes { - // default route is + // default route is handled by route exclusion / ip rules if prefix.Bits() == 0 { continue } diff --git a/client/internal/routemanager/iface/iface_common.go b/client/internal/routemanager/iface/iface_common.go index 8b2dc9714..9e1f8058a 100644 --- a/client/internal/routemanager/iface/iface_common.go +++ b/client/internal/routemanager/iface/iface_common.go @@ -3,9 +3,9 @@ package iface import ( "net" - "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type wgIfaceBase interface { @@ -13,7 +13,7 @@ type wgIfaceBase interface { RemoveAllowedIP(peerKey string, allowedIP string) error Name() string - Address() iface.WGAddress + Address() wgaddr.Address ToInterface() *net.Interface IsUserspaceBind() bool GetFilter() device.PacketFilter From 47dcf8d68cd9e58220151a95e2f0c8fc11bae77f Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:55:07 +0100 Subject: [PATCH 27/30] Fix forwarder IP source/destination (#3463) --- client/firewall/uspfilter/forwarder/icmp.go | 4 +-- client/firewall/uspfilter/forwarder/tcp.go | 8 +++--- client/firewall/uspfilter/forwarder/udp.go | 29 +++++---------------- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/client/firewall/uspfilter/forwarder/icmp.go b/client/firewall/uspfilter/forwarder/icmp.go index 833854a21..a21ec2c87 100644 --- a/client/firewall/uspfilter/forwarder/icmp.go +++ b/client/firewall/uspfilter/forwarder/icmp.go @@ -117,8 +117,8 @@ func (f *Forwarder) sendICMPEvent(typ nftypes.Type, flowID uuid.UUID, id stack.T Direction: nftypes.Ingress, Protocol: nftypes.ICMP, // TODO: handle ipv6 - SourceIP: netip.AddrFrom4(id.LocalAddress.As4()), - DestIP: netip.AddrFrom4(id.RemoteAddress.As4()), + SourceIP: netip.AddrFrom4(id.RemoteAddress.As4()), + DestIP: netip.AddrFrom4(id.LocalAddress.As4()), ICMPType: icmpType, ICMPCode: icmpCode, diff --git a/client/firewall/uspfilter/forwarder/tcp.go b/client/firewall/uspfilter/forwarder/tcp.go index c3e1eca80..71cd457ef 100644 --- a/client/firewall/uspfilter/forwarder/tcp.go +++ b/client/firewall/uspfilter/forwarder/tcp.go @@ -113,10 +113,10 @@ func (f *Forwarder) sendTCPEvent(typ nftypes.Type, flowID uuid.UUID, id stack.Tr Direction: nftypes.Ingress, Protocol: nftypes.TCP, // TODO: handle ipv6 - SourceIP: netip.AddrFrom4(id.LocalAddress.As4()), - DestIP: netip.AddrFrom4(id.RemoteAddress.As4()), - SourcePort: id.LocalPort, - DestPort: id.RemotePort, + SourceIP: netip.AddrFrom4(id.RemoteAddress.As4()), + DestIP: netip.AddrFrom4(id.LocalAddress.As4()), + SourcePort: id.RemotePort, + DestPort: id.LocalPort, } if ep != nil { diff --git a/client/firewall/uspfilter/forwarder/udp.go b/client/firewall/uspfilter/forwarder/udp.go index 20e1ee3a7..7ce85e2b6 100644 --- a/client/firewall/uspfilter/forwarder/udp.go +++ b/client/firewall/uspfilter/forwarder/udp.go @@ -89,21 +89,6 @@ func (f *udpForwarder) Stop() { } } -// sendUDPEvent stores flow events for UDP connections -func (f *udpForwarder) sendUDPEvent(typ nftypes.Type, flowID uuid.UUID, id stack.TransportEndpointID) { - f.flowLogger.StoreEvent(nftypes.EventFields{ - FlowID: flowID, - Type: typ, - Direction: nftypes.Ingress, - Protocol: 17, - // TODO: handle ipv6 - SourceIP: netip.AddrFrom4(id.LocalAddress.As4()), - DestIP: netip.AddrFrom4(id.RemoteAddress.As4()), - SourcePort: id.LocalPort, - DestPort: id.RemotePort, - }) -} - // cleanup periodically removes idle UDP connections func (f *udpForwarder) cleanup() { ticker := time.NewTicker(time.Minute) @@ -140,8 +125,6 @@ func (f *udpForwarder) cleanup() { f.Unlock() f.logger.Trace("forwarder: cleaned up idle UDP connection %v", epID(idle.id)) - - f.sendUDPEvent(nftypes.TypeEnd, idle.conn.flowID, idle.id) } } } @@ -270,18 +253,18 @@ func (f *Forwarder) proxyUDP(ctx context.Context, pConn *udpPacketConn, id stack } } -// sendUDPEvent stores flow events for UDP connections, mirrors the TCP version +// sendUDPEvent stores flow events for UDP connections func (f *Forwarder) sendUDPEvent(typ nftypes.Type, flowID uuid.UUID, id stack.TransportEndpointID, ep tcpip.Endpoint) { fields := nftypes.EventFields{ FlowID: flowID, Type: typ, Direction: nftypes.Ingress, - Protocol: 17, // UDP protocol number + Protocol: nftypes.UDP, // TODO: handle ipv6 - SourceIP: netip.AddrFrom4(id.LocalAddress.As4()), - DestIP: netip.AddrFrom4(id.RemoteAddress.As4()), - SourcePort: id.LocalPort, - DestPort: id.RemotePort, + SourceIP: netip.AddrFrom4(id.RemoteAddress.As4()), + DestIP: netip.AddrFrom4(id.LocalAddress.As4()), + SourcePort: id.RemotePort, + DestPort: id.LocalPort, } if ep != nil { From cd9eff5331578e68299310bade39043b218e78d7 Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Mon, 10 Mar 2025 18:23:47 +0100 Subject: [PATCH 28/30] Increase the timeout to 50 sec (#3481) --- client/server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/server/server.go b/client/server/server.go index b112a994f..2d8f759cd 100644 --- a/client/server/server.go +++ b/client/server/server.go @@ -628,7 +628,7 @@ func (s *Server) Up(callerCtx context.Context, _ *proto.UpRequest) (*proto.UpRes s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String()) s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive) - timeoutCtx, cancel := context.WithTimeout(callerCtx, 10*time.Second) + timeoutCtx, cancel := context.WithTimeout(callerCtx, 50*time.Second) defer cancel() runningChan := make(chan struct{}, 1) // buffered channel to do not lose the signal From 11828a064a4b561ce8e0c3f7b55207563a0b097f Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Mon, 10 Mar 2025 18:35:32 +0100 Subject: [PATCH 29/30] Fix conflict --- client/internal/netflow/types/types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/internal/netflow/types/types.go b/client/internal/netflow/types/types.go index fc90a53d6..1e61038cf 100644 --- a/client/internal/netflow/types/types.go +++ b/client/internal/netflow/types/types.go @@ -7,7 +7,7 @@ import ( "github.com/google/uuid" - "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) type Protocol uint8 @@ -145,5 +145,5 @@ type ConnTracker interface { type IFaceMapper interface { IsUserspaceBind() bool Name() string - Address() device.WGAddress + Address() wgaddr.Address } From 76d73548d623c42ad47f5e8d2297d5f32f6f9dcd Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Mon, 10 Mar 2025 18:46:01 +0100 Subject: [PATCH 30/30] Fix more conflicts --- client/firewall/uspfilter/tracer_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/firewall/uspfilter/tracer_test.go b/client/firewall/uspfilter/tracer_test.go index d8269f254..48b0ec44d 100644 --- a/client/firewall/uspfilter/tracer_test.go +++ b/client/firewall/uspfilter/tracer_test.go @@ -10,8 +10,8 @@ import ( fw "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack" "github.com/netbirdio/netbird/client/firewall/uspfilter/forwarder" - "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/device" + "github.com/netbirdio/netbird/client/iface/wgaddr" ) func verifyTraceStages(t *testing.T, trace *PacketTrace, expectedStages []PacketStage) { @@ -36,8 +36,8 @@ func TestTracePacket(t *testing.T) { setupTracerTest := func(statefulMode bool) *Manager { ifaceMock := &IFaceMock{ SetFilterFunc: func(device.PacketFilter) error { return nil }, - AddressFunc: func() iface.WGAddress { - return iface.WGAddress{ + AddressFunc: func() wgaddr.Address { + return wgaddr.Address{ IP: net.ParseIP("100.10.0.100"), Network: &net.IPNet{ IP: net.ParseIP("100.10.0.0"),