mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-25 19:56:46 +00:00
Compare commits
12 Commits
debug-and-
...
dns-exit-n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2db23a42dc | ||
|
|
c2822eebb0 | ||
|
|
5b246e0a08 | ||
|
|
7aef0f67df | ||
|
|
dba7ef667d | ||
|
|
69d87343d2 | ||
|
|
5113c70943 | ||
|
|
ad8fcda67b | ||
|
|
d33f88df82 | ||
|
|
786ca6fc79 | ||
|
|
dfebdf1444 | ||
|
|
a8dcff69c2 |
@@ -4,6 +4,7 @@ package android
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -83,7 +84,8 @@ func NewClient(cfgFile string, androidSDKVersion int, deviceName string, uiVersi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run start the internal client. It is a blocker function
|
// Run start the internal client. It is a blocker function
|
||||||
func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsReadyListener) error {
|
func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsReadyListener, envList *EnvList) error {
|
||||||
|
exportEnvList(envList)
|
||||||
cfg, err := profilemanager.UpdateOrCreateConfig(profilemanager.ConfigInput{
|
cfg, err := profilemanager.UpdateOrCreateConfig(profilemanager.ConfigInput{
|
||||||
ConfigPath: c.cfgFile,
|
ConfigPath: c.cfgFile,
|
||||||
})
|
})
|
||||||
@@ -118,7 +120,8 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead
|
|||||||
|
|
||||||
// RunWithoutLogin we apply this type of run function when the backed has been started without UI (i.e. after reboot).
|
// RunWithoutLogin we apply this type of run function when the backed has been started without UI (i.e. after reboot).
|
||||||
// In this case make no sense handle registration steps.
|
// In this case make no sense handle registration steps.
|
||||||
func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener) error {
|
func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener, envList *EnvList) error {
|
||||||
|
exportEnvList(envList)
|
||||||
cfg, err := profilemanager.UpdateOrCreateConfig(profilemanager.ConfigInput{
|
cfg, err := profilemanager.UpdateOrCreateConfig(profilemanager.ConfigInput{
|
||||||
ConfigPath: c.cfgFile,
|
ConfigPath: c.cfgFile,
|
||||||
})
|
})
|
||||||
@@ -249,3 +252,14 @@ func (c *Client) SetConnectionListener(listener ConnectionListener) {
|
|||||||
func (c *Client) RemoveConnectionListener() {
|
func (c *Client) RemoveConnectionListener() {
|
||||||
c.recorder.RemoveConnectionListener()
|
c.recorder.RemoveConnectionListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func exportEnvList(list *EnvList) {
|
||||||
|
if list == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, v := range list.AllItems() {
|
||||||
|
if err := os.Setenv(k, v); err != nil {
|
||||||
|
log.Errorf("could not set env variable %s: %v", k, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
32
client/android/env_list.go
Normal file
32
client/android/env_list.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package android
|
||||||
|
|
||||||
|
import "github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// EnvKeyNBForceRelay Exported for Android java client
|
||||||
|
EnvKeyNBForceRelay = peer.EnvKeyNBForceRelay
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnvList wraps a Go map for export to Java
|
||||||
|
type EnvList struct {
|
||||||
|
data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEnvList creates a new EnvList
|
||||||
|
func NewEnvList() *EnvList {
|
||||||
|
return &EnvList{data: make(map[string]string)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put adds a key-value pair
|
||||||
|
func (el *EnvList) Put(key, value string) {
|
||||||
|
el.data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves a value by key
|
||||||
|
func (el *EnvList) Get(key string) string {
|
||||||
|
return el.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *EnvList) AllItems() map[string]string {
|
||||||
|
return el.data
|
||||||
|
}
|
||||||
@@ -388,12 +388,12 @@ func generateDebugBundle(config *profilemanager.Config, recorder *peer.Status, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
debugBundleCmd.Flags().Uint32VarP(&logFileCount, "log-file-count", "C", 1, "Number of rotated log files to include in debug bundle")
|
debugBundleCmd.Flags().Uint32VarP(&logFileCount, "log-file-count", "C", 10, "Number of rotated log files to include in debug bundle")
|
||||||
debugBundleCmd.Flags().BoolVarP(&systemInfoFlag, "system-info", "S", true, "Adds system information to the debug bundle")
|
debugBundleCmd.Flags().BoolVarP(&systemInfoFlag, "system-info", "S", true, "Adds system information to the debug bundle")
|
||||||
debugBundleCmd.Flags().BoolVarP(&uploadBundleFlag, "upload-bundle", "U", false, "Uploads the debug bundle to a server")
|
debugBundleCmd.Flags().BoolVarP(&uploadBundleFlag, "upload-bundle", "U", false, "Uploads the debug bundle to a server")
|
||||||
debugBundleCmd.Flags().StringVar(&uploadBundleURLFlag, "upload-bundle-url", types.DefaultBundleURL, "Service URL to get an URL to upload the debug bundle")
|
debugBundleCmd.Flags().StringVar(&uploadBundleURLFlag, "upload-bundle-url", types.DefaultBundleURL, "Service URL to get an URL to upload the debug bundle")
|
||||||
|
|
||||||
forCmd.Flags().Uint32VarP(&logFileCount, "log-file-count", "C", 1, "Number of rotated log files to include in debug bundle")
|
forCmd.Flags().Uint32VarP(&logFileCount, "log-file-count", "C", 10, "Number of rotated log files to include in debug bundle")
|
||||||
forCmd.Flags().BoolVarP(&systemInfoFlag, "system-info", "S", true, "Adds system information to the debug bundle")
|
forCmd.Flags().BoolVarP(&systemInfoFlag, "system-info", "S", true, "Adds system information to the debug bundle")
|
||||||
forCmd.Flags().BoolVarP(&uploadBundleFlag, "upload-bundle", "U", false, "Uploads the debug bundle to a server")
|
forCmd.Flags().BoolVarP(&uploadBundleFlag, "upload-bundle", "U", false, "Uploads the debug bundle to a server")
|
||||||
forCmd.Flags().StringVar(&uploadBundleURLFlag, "upload-bundle-url", types.DefaultBundleURL, "Service URL to get an URL to upload the debug bundle")
|
forCmd.Flags().StringVar(&uploadBundleURLFlag, "upload-bundle-url", types.DefaultBundleURL, "Service URL to get an URL to upload the debug bundle")
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ func doForegroundLogin(ctx context.Context, cmd *cobra.Command, setupKey string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update host's static platform and system information
|
// update host's static platform and system information
|
||||||
system.UpdateStaticInfo()
|
system.UpdateStaticInfoAsync()
|
||||||
|
|
||||||
configFilePath, err := activeProf.FilePath()
|
configFilePath, err := activeProf.FilePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func (p *program) Start(svc service.Service) error {
|
|||||||
log.Info("starting NetBird service") //nolint
|
log.Info("starting NetBird service") //nolint
|
||||||
|
|
||||||
// Collect static system and platform information
|
// Collect static system and platform information
|
||||||
system.UpdateStaticInfo()
|
system.UpdateStaticInfoAsync()
|
||||||
|
|
||||||
// in any case, even if configuration does not exists we run daemon to serve CLI gRPC API.
|
// in any case, even if configuration does not exists we run daemon to serve CLI gRPC API.
|
||||||
p.serv = grpc.NewServer()
|
p.serv = grpc.NewServer()
|
||||||
|
|||||||
@@ -9,29 +9,26 @@ import (
|
|||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
|
clientProto "github.com/netbirdio/netbird/client/proto"
|
||||||
|
client "github.com/netbirdio/netbird/client/server"
|
||||||
"github.com/netbirdio/netbird/management/internals/server/config"
|
"github.com/netbirdio/netbird/management/internals/server/config"
|
||||||
|
mgmt "github.com/netbirdio/netbird/management/server"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
"github.com/netbirdio/netbird/management/server/groups"
|
"github.com/netbirdio/netbird/management/server/groups"
|
||||||
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
||||||
|
"github.com/netbirdio/netbird/management/server/peers"
|
||||||
"github.com/netbirdio/netbird/management/server/permissions"
|
"github.com/netbirdio/netbird/management/server/permissions"
|
||||||
"github.com/netbirdio/netbird/management/server/settings"
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
"github.com/netbirdio/netbird/management/server/store"
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
"github.com/netbirdio/netbird/management/server/types"
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/util"
|
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
|
|
||||||
"github.com/netbirdio/management-integrations/integrations"
|
|
||||||
|
|
||||||
clientProto "github.com/netbirdio/netbird/client/proto"
|
|
||||||
client "github.com/netbirdio/netbird/client/server"
|
|
||||||
mgmt "github.com/netbirdio/netbird/management/server"
|
|
||||||
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
|
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||||
sigProto "github.com/netbirdio/netbird/shared/signal/proto"
|
sigProto "github.com/netbirdio/netbird/shared/signal/proto"
|
||||||
sig "github.com/netbirdio/netbird/signal/server"
|
sig "github.com/netbirdio/netbird/signal/server"
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startTestingServices(t *testing.T) string {
|
func startTestingServices(t *testing.T) string {
|
||||||
@@ -90,15 +87,20 @@ func startManagement(t *testing.T, config *config.Config, testFile string) (*grp
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
iv, _ := integrations.NewIntegratedValidator(context.Background(), eventStore)
|
|
||||||
|
|
||||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
|
||||||
require.NoError(t, err)
|
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
t.Cleanup(ctrl.Finish)
|
t.Cleanup(ctrl.Finish)
|
||||||
|
|
||||||
settingsMockManager := settings.NewMockManager(ctrl)
|
|
||||||
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
||||||
|
peersmanager := peers.NewManager(store, permissionsManagerMock)
|
||||||
|
settingsManagerMock := settings.NewMockManager(ctrl)
|
||||||
|
|
||||||
|
iv, _ := integrations.NewIntegratedValidator(context.Background(), peersmanager, settingsManagerMock, eventStore)
|
||||||
|
|
||||||
|
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
settingsMockManager := settings.NewMockManager(ctrl)
|
||||||
groupsManager := groups.NewManagerMock()
|
groupsManager := groups.NewManagerMock()
|
||||||
|
|
||||||
settingsMockManager.EXPECT().
|
settingsMockManager.EXPECT().
|
||||||
|
|||||||
@@ -394,6 +394,13 @@ func toLastHandshake(stringVar string) (time.Time, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, fmt.Errorf("parse handshake sec: %w", err)
|
return time.Time{}, fmt.Errorf("parse handshake sec: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If sec is 0 (Unix epoch), return zero time instead
|
||||||
|
// This indicates no handshake has occurred
|
||||||
|
if sec == 0 {
|
||||||
|
return time.Time{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
return time.Unix(sec, 0), nil
|
return time.Unix(sec, 0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -315,6 +315,10 @@ func (g *BundleGenerator) createArchive() error {
|
|||||||
return fmt.Errorf("add sync response: %w", err)
|
return fmt.Errorf("add sync response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := g.addDNSConfig(); err != nil {
|
||||||
|
log.Errorf("failed to add DNS config to debug bundle: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := g.addStateFile(); err != nil {
|
if err := g.addStateFile(); err != nil {
|
||||||
log.Errorf("failed to add state file to debug bundle: %v", err)
|
log.Errorf("failed to add state file to debug bundle: %v", err)
|
||||||
}
|
}
|
||||||
@@ -341,6 +345,50 @@ func (g *BundleGenerator) createArchive() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addDNSConfig writes a dns_config.json snapshot with routed domains and NS group status
|
||||||
|
func (g *BundleGenerator) addDNSConfig() error {
|
||||||
|
type nsGroup struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Servers []string `json:"servers"`
|
||||||
|
Domains []string `json:"domains"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
type dnsConfig struct {
|
||||||
|
Groups []nsGroup `json:"name_server_groups"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.statusRecorder == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
states := g.statusRecorder.GetDNSStates()
|
||||||
|
cfg := dnsConfig{Groups: make([]nsGroup, 0, len(states))}
|
||||||
|
for _, st := range states {
|
||||||
|
var servers []string
|
||||||
|
for _, ap := range st.Servers {
|
||||||
|
servers = append(servers, ap.String())
|
||||||
|
}
|
||||||
|
var errStr string
|
||||||
|
if st.Error != nil {
|
||||||
|
errStr = st.Error.Error()
|
||||||
|
}
|
||||||
|
cfg.Groups = append(cfg.Groups, nsGroup{
|
||||||
|
ID: st.ID,
|
||||||
|
Servers: servers,
|
||||||
|
Domains: st.Domains,
|
||||||
|
Enabled: st.Enabled,
|
||||||
|
Error: errStr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
bs, err := json.MarshalIndent(cfg, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshal dns config: %w", err)
|
||||||
|
}
|
||||||
|
return g.addFileToZip(bytes.NewReader(bs), "dns_config.json")
|
||||||
|
}
|
||||||
|
|
||||||
func (g *BundleGenerator) addSystemInfo() {
|
func (g *BundleGenerator) addSystemInfo() {
|
||||||
if err := g.addRoutes(); err != nil {
|
if err := g.addRoutes(); err != nil {
|
||||||
log.Errorf("failed to add routes to debug bundle: %v", err)
|
log.Errorf("failed to add routes to debug bundle: %v", err)
|
||||||
|
|||||||
@@ -46,6 +46,18 @@ type DNSForwarder struct {
|
|||||||
fwdEntries []*ForwarderEntry
|
fwdEntries []*ForwarderEntry
|
||||||
firewall firewaller
|
firewall firewaller
|
||||||
resolver resolver
|
resolver resolver
|
||||||
|
|
||||||
|
// failure rate tracking for routed domains
|
||||||
|
failureMu sync.Mutex
|
||||||
|
failureCounts map[string]int
|
||||||
|
failureWindow time.Duration
|
||||||
|
lastLogPerHost map[string]time.Time
|
||||||
|
|
||||||
|
// per-domain rolling stats and windows
|
||||||
|
statsMu sync.Mutex
|
||||||
|
stats map[string]*domainStats
|
||||||
|
winSize time.Duration
|
||||||
|
slowT time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDNSForwarder(listenAddress string, ttl uint32, firewall firewaller, statusRecorder *peer.Status) *DNSForwarder {
|
func NewDNSForwarder(listenAddress string, ttl uint32, firewall firewaller, statusRecorder *peer.Status) *DNSForwarder {
|
||||||
@@ -56,9 +68,25 @@ func NewDNSForwarder(listenAddress string, ttl uint32, firewall firewaller, stat
|
|||||||
firewall: firewall,
|
firewall: firewall,
|
||||||
statusRecorder: statusRecorder,
|
statusRecorder: statusRecorder,
|
||||||
resolver: net.DefaultResolver,
|
resolver: net.DefaultResolver,
|
||||||
|
failureCounts: make(map[string]int),
|
||||||
|
failureWindow: 10 * time.Second,
|
||||||
|
lastLogPerHost: make(map[string]time.Time),
|
||||||
|
stats: make(map[string]*domainStats),
|
||||||
|
winSize: 10 * time.Second,
|
||||||
|
slowT: 300 * time.Millisecond,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type domainStats struct {
|
||||||
|
total int
|
||||||
|
success int
|
||||||
|
timeouts int
|
||||||
|
notfound int
|
||||||
|
failures int // other failures (incl. SERVFAIL-like)
|
||||||
|
slow int
|
||||||
|
lastLog time.Time
|
||||||
|
}
|
||||||
|
|
||||||
func (f *DNSForwarder) Listen(entries []*ForwarderEntry) error {
|
func (f *DNSForwarder) Listen(entries []*ForwarderEntry) error {
|
||||||
log.Infof("starting DNS forwarder on address=%s", f.listenAddress)
|
log.Infof("starting DNS forwarder on address=%s", f.listenAddress)
|
||||||
|
|
||||||
@@ -163,12 +191,19 @@ func (f *DNSForwarder) handleDNSQuery(w dns.ResponseWriter, query *dns.Msg) *dns
|
|||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), upstreamTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), upstreamTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
start := time.Now()
|
||||||
ips, err := f.resolver.LookupNetIP(ctx, network, domain)
|
ips, err := f.resolver.LookupNetIP(ctx, network, domain)
|
||||||
|
elapsed := time.Since(start)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.handleDNSError(ctx, w, question, resp, domain, err)
|
f.handleDNSError(ctx, w, question, resp, domain, err)
|
||||||
|
// record error stats for routed domains
|
||||||
|
f.recordErrorStats(strings.TrimSuffix(domain, "."), err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// record success timing
|
||||||
|
f.recordSuccessStats(strings.TrimSuffix(domain, "."), elapsed)
|
||||||
|
|
||||||
f.updateInternalState(ips, mostSpecificResId, matchingEntries)
|
f.updateInternalState(ips, mostSpecificResId, matchingEntries)
|
||||||
f.addIPsToResponse(resp, domain, ips)
|
f.addIPsToResponse(resp, domain, ips)
|
||||||
|
|
||||||
@@ -306,6 +341,91 @@ func (f *DNSForwarder) handleDNSError(ctx context.Context, w dns.ResponseWriter,
|
|||||||
if err := w.WriteMsg(resp); err != nil {
|
if err := w.WriteMsg(resp); err != nil {
|
||||||
log.Errorf("failed to write failure DNS response: %v", err)
|
log.Errorf("failed to write failure DNS response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track failure rate for routed domains only
|
||||||
|
if resID, _ := f.getMatchingEntries(strings.TrimSuffix(domain, ".")); resID != "" {
|
||||||
|
f.recordDomainFailure(strings.TrimSuffix(domain, "."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recordErrorStats updates per-domain counters and emits rate-limited logs
|
||||||
|
func (f *DNSForwarder) recordErrorStats(domain string, err error) {
|
||||||
|
domain = strings.ToLower(domain)
|
||||||
|
f.statsMu.Lock()
|
||||||
|
s := f.ensureStats(domain)
|
||||||
|
s.total++
|
||||||
|
|
||||||
|
var dnsErr *net.DNSError
|
||||||
|
if errors.As(err, &dnsErr) {
|
||||||
|
if dnsErr.IsNotFound {
|
||||||
|
s.notfound++
|
||||||
|
} else if dnsErr.Timeout() {
|
||||||
|
s.timeouts++
|
||||||
|
} else {
|
||||||
|
s.failures++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.failures++
|
||||||
|
}
|
||||||
|
|
||||||
|
f.maybeLogDomainStats(domain, s)
|
||||||
|
f.statsMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// recordSuccessStats updates per-domain latency stats and slow counters, logs if needed (rate-limited)
|
||||||
|
func (f *DNSForwarder) recordSuccessStats(domain string, elapsed time.Duration) {
|
||||||
|
domain = strings.ToLower(domain)
|
||||||
|
f.statsMu.Lock()
|
||||||
|
s := f.ensureStats(domain)
|
||||||
|
s.total++
|
||||||
|
s.success++
|
||||||
|
if elapsed >= f.slowT {
|
||||||
|
s.slow++
|
||||||
|
}
|
||||||
|
f.maybeLogDomainStats(domain, s)
|
||||||
|
f.statsMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *DNSForwarder) ensureStats(domain string) *domainStats {
|
||||||
|
if ds, ok := f.stats[domain]; ok {
|
||||||
|
return ds
|
||||||
|
}
|
||||||
|
ds := &domainStats{}
|
||||||
|
f.stats[domain] = ds
|
||||||
|
return ds
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybeLogDomainStats logs a compact summary per routed domain at most once per window
|
||||||
|
func (f *DNSForwarder) maybeLogDomainStats(domain string, s *domainStats) {
|
||||||
|
now := time.Now()
|
||||||
|
if !s.lastLog.IsZero() && now.Sub(s.lastLog) < f.winSize {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if routed (avoid logging for non-routed domains)
|
||||||
|
if resID, _ := f.getMatchingEntries(domain); resID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// only log if something noteworthy happened in the window
|
||||||
|
noteworthy := s.timeouts > 0 || s.notfound > 0 || s.failures > 0 || s.slow > 0
|
||||||
|
if !noteworthy {
|
||||||
|
s.lastLog = now
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// warn on persistent problems, info otherwise
|
||||||
|
levelWarn := s.timeouts >= 3 || s.failures >= 3
|
||||||
|
if levelWarn {
|
||||||
|
log.Warnf("[d] DNS stats: domain=%s total=%d ok=%d timeout=%d nxdomain=%d fail=%d slow=%d(>=%s)",
|
||||||
|
domain, s.total, s.success, s.timeouts, s.notfound, s.failures, s.slow, f.slowT)
|
||||||
|
} else {
|
||||||
|
log.Infof("[d] DNS stats: domain=%s total=%d ok=%d timeout=%d nxdomain=%d fail=%d slow=%d(>=%s)",
|
||||||
|
domain, s.total, s.success, s.timeouts, s.notfound, s.failures, s.slow, f.slowT)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset counters for next window
|
||||||
|
*s = domainStats{lastLog: now}
|
||||||
}
|
}
|
||||||
|
|
||||||
// addIPsToResponse adds IP addresses to the DNS response as appropriate A or AAAA records
|
// addIPsToResponse adds IP addresses to the DNS response as appropriate A or AAAA records
|
||||||
@@ -341,6 +461,27 @@ func (f *DNSForwarder) addIPsToResponse(resp *dns.Msg, domain string, ips []neti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// recordDomainFailure increments failure count for the domain and logs at info/warn with throttling.
|
||||||
|
func (f *DNSForwarder) recordDomainFailure(domain string) {
|
||||||
|
domain = strings.ToLower(domain)
|
||||||
|
|
||||||
|
f.failureMu.Lock()
|
||||||
|
defer f.failureMu.Unlock()
|
||||||
|
|
||||||
|
f.failureCounts[domain]++
|
||||||
|
count := f.failureCounts[domain]
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
last, ok := f.lastLogPerHost[domain]
|
||||||
|
if ok && now.Sub(last) < f.failureWindow {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.lastLogPerHost[domain] = now
|
||||||
|
|
||||||
|
log.Warnf("[d] DNS failures observed for routed domain: domain=%s failures=%d/%s", domain, count, f.failureWindow)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// getMatchingEntries retrieves the resource IDs for a given domain.
|
// getMatchingEntries retrieves the resource IDs for a given domain.
|
||||||
// It returns the most specific match and all matching resource IDs.
|
// It returns the most specific match and all matching resource IDs.
|
||||||
func (f *DNSForwarder) getMatchingEntries(domain string) (route.ResID, []*ForwarderEntry) {
|
func (f *DNSForwarder) getMatchingEntries(domain string) (route.ResID, []*ForwarderEntry) {
|
||||||
|
|||||||
@@ -949,7 +949,6 @@ func (e *Engine) receiveManagementEvents() {
|
|||||||
e.config.LazyConnectionEnabled,
|
e.config.LazyConnectionEnabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
// err = e.mgmClient.Sync(info, e.handleSync)
|
|
||||||
err = e.mgmClient.Sync(e.ctx, info, e.handleSync)
|
err = e.mgmClient.Sync(e.ctx, info, e.handleSync)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// happens if management is unavailable for a long time.
|
// happens if management is unavailable for a long time.
|
||||||
@@ -960,7 +959,7 @@ func (e *Engine) receiveManagementEvents() {
|
|||||||
}
|
}
|
||||||
log.Debugf("stopped receiving updates from Management Service")
|
log.Debugf("stopped receiving updates from Management Service")
|
||||||
}()
|
}()
|
||||||
log.Debugf("connecting to Management Service updates stream")
|
log.Infof("connecting to Management Service updates stream")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) updateSTUNs(stuns []*mgmProto.HostConfig) error {
|
func (e *Engine) updateSTUNs(stuns []*mgmProto.HostConfig) error {
|
||||||
|
|||||||
@@ -19,17 +19,13 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
|
wgdevice "golang.zx2c4.com/wireguard/device"
|
||||||
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
|
||||||
wgdevice "golang.zx2c4.com/wireguard/device"
|
|
||||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
|
||||||
|
|
||||||
"github.com/netbirdio/management-integrations/integrations"
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
"github.com/netbirdio/netbird/management/internals/server/config"
|
|
||||||
"github.com/netbirdio/netbird/management/server/groups"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
"github.com/netbirdio/netbird/client/iface/bind"
|
"github.com/netbirdio/netbird/client/iface/bind"
|
||||||
"github.com/netbirdio/netbird/client/iface/configurer"
|
"github.com/netbirdio/netbird/client/iface/configurer"
|
||||||
@@ -45,9 +41,12 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/ssh"
|
"github.com/netbirdio/netbird/client/ssh"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
"github.com/netbirdio/netbird/management/internals/server/config"
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/groups"
|
||||||
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
||||||
|
"github.com/netbirdio/netbird/management/server/peers"
|
||||||
"github.com/netbirdio/netbird/management/server/permissions"
|
"github.com/netbirdio/netbird/management/server/permissions"
|
||||||
"github.com/netbirdio/netbird/management/server/settings"
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
"github.com/netbirdio/netbird/management/server/store"
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
@@ -1555,7 +1554,11 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
ia, _ := integrations.NewIntegratedValidator(context.Background(), eventStore)
|
|
||||||
|
permissionsManager := permissions.NewManager(store)
|
||||||
|
peersManager := peers.NewManager(store, permissionsManager)
|
||||||
|
|
||||||
|
ia, _ := integrations.NewIntegratedValidator(context.Background(), peersManager, nil, eventStore)
|
||||||
|
|
||||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -1572,7 +1575,6 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
|
|||||||
Return(&types.ExtraSettings{}, nil).
|
Return(&types.ExtraSettings{}, nil).
|
||||||
AnyTimes()
|
AnyTimes()
|
||||||
|
|
||||||
permissionsManager := permissions.NewManager(store)
|
|
||||||
groupsManager := groups.NewManagerMock()
|
groupsManager := groups.NewManagerMock()
|
||||||
|
|
||||||
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
|
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -174,7 +173,7 @@ func (conn *Conn) Open(engineCtx context.Context) error {
|
|||||||
conn.handshaker = NewHandshaker(conn.Log, conn.config, conn.signaler, conn.workerICE, conn.workerRelay)
|
conn.handshaker = NewHandshaker(conn.Log, conn.config, conn.signaler, conn.workerICE, conn.workerRelay)
|
||||||
|
|
||||||
conn.handshaker.AddOnNewOfferListener(conn.workerRelay.OnNewOffer)
|
conn.handshaker.AddOnNewOfferListener(conn.workerRelay.OnNewOffer)
|
||||||
if os.Getenv("NB_FORCE_RELAY") != "true" {
|
if !isForceRelayed() {
|
||||||
conn.handshaker.AddOnNewOfferListener(conn.workerICE.OnNewOffer)
|
conn.handshaker.AddOnNewOfferListener(conn.workerICE.OnNewOffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
client/internal/peer/env.go
Normal file
14
client/internal/peer/env.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EnvKeyNBForceRelay = "NB_FORCE_RELAY"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isForceRelayed() bool {
|
||||||
|
return strings.EqualFold(os.Getenv(EnvKeyNBForceRelay), "true")
|
||||||
|
}
|
||||||
@@ -43,13 +43,6 @@ type OfferAnswer struct {
|
|||||||
SessionID *ICESessionID
|
SessionID *ICESessionID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oa *OfferAnswer) SessionIDString() string {
|
|
||||||
if oa.SessionID == nil {
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
return oa.SessionID.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Handshaker struct {
|
type Handshaker struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
log *log.Entry
|
log *log.Entry
|
||||||
@@ -57,7 +50,7 @@ type Handshaker struct {
|
|||||||
signaler *Signaler
|
signaler *Signaler
|
||||||
ice *WorkerICE
|
ice *WorkerICE
|
||||||
relay *WorkerRelay
|
relay *WorkerRelay
|
||||||
onNewOfferListeners []func(*OfferAnswer)
|
onNewOfferListeners []*OfferListener
|
||||||
|
|
||||||
// remoteOffersCh is a channel used to wait for remote credentials to proceed with the connection
|
// remoteOffersCh is a channel used to wait for remote credentials to proceed with the connection
|
||||||
remoteOffersCh chan OfferAnswer
|
remoteOffersCh chan OfferAnswer
|
||||||
@@ -78,7 +71,8 @@ func NewHandshaker(log *log.Entry, config ConnConfig, signaler *Signaler, ice *W
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handshaker) AddOnNewOfferListener(offer func(remoteOfferAnswer *OfferAnswer)) {
|
func (h *Handshaker) AddOnNewOfferListener(offer func(remoteOfferAnswer *OfferAnswer)) {
|
||||||
h.onNewOfferListeners = append(h.onNewOfferListeners, offer)
|
l := NewOfferListener(offer)
|
||||||
|
h.onNewOfferListeners = append(h.onNewOfferListeners, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handshaker) Listen(ctx context.Context) {
|
func (h *Handshaker) Listen(ctx context.Context) {
|
||||||
@@ -91,13 +85,13 @@ func (h *Handshaker) Listen(ctx context.Context) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, listener := range h.onNewOfferListeners {
|
for _, listener := range h.onNewOfferListeners {
|
||||||
listener(&remoteOfferAnswer)
|
listener.Notify(&remoteOfferAnswer)
|
||||||
}
|
}
|
||||||
h.log.Infof("received offer, running version %s, remote WireGuard listen port %d, session id: %s", remoteOfferAnswer.Version, remoteOfferAnswer.WgListenPort, remoteOfferAnswer.SessionIDString())
|
h.log.Infof("received offer, running version %s, remote WireGuard listen port %d, session id: %s", remoteOfferAnswer.Version, remoteOfferAnswer.WgListenPort, remoteOfferAnswer.SessionIDString())
|
||||||
case remoteOfferAnswer := <-h.remoteAnswerCh:
|
case remoteOfferAnswer := <-h.remoteAnswerCh:
|
||||||
h.log.Infof("received answer, running version %s, remote WireGuard listen port %d, session id: %s", remoteOfferAnswer.Version, remoteOfferAnswer.WgListenPort, remoteOfferAnswer.SessionIDString())
|
h.log.Infof("received answer, running version %s, remote WireGuard listen port %d, session id: %s", remoteOfferAnswer.Version, remoteOfferAnswer.WgListenPort, remoteOfferAnswer.SessionIDString())
|
||||||
for _, listener := range h.onNewOfferListeners {
|
for _, listener := range h.onNewOfferListeners {
|
||||||
listener(&remoteOfferAnswer)
|
listener.Notify(&remoteOfferAnswer)
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
h.log.Infof("stop listening for remote offers and answers")
|
h.log.Infof("stop listening for remote offers and answers")
|
||||||
|
|||||||
62
client/internal/peer/handshaker_listener.go
Normal file
62
client/internal/peer/handshaker_listener.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type callbackFunc func(remoteOfferAnswer *OfferAnswer)
|
||||||
|
|
||||||
|
func (oa *OfferAnswer) SessionIDString() string {
|
||||||
|
if oa.SessionID == nil {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
return oa.SessionID.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type OfferListener struct {
|
||||||
|
fn callbackFunc
|
||||||
|
running bool
|
||||||
|
latest *OfferAnswer
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOfferListener(fn callbackFunc) *OfferListener {
|
||||||
|
return &OfferListener{
|
||||||
|
fn: fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OfferListener) Notify(remoteOfferAnswer *OfferAnswer) {
|
||||||
|
o.mu.Lock()
|
||||||
|
defer o.mu.Unlock()
|
||||||
|
|
||||||
|
// Store the latest offer
|
||||||
|
o.latest = remoteOfferAnswer
|
||||||
|
|
||||||
|
// If already running, the running goroutine will pick up this latest value
|
||||||
|
if o.running {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start processing
|
||||||
|
o.running = true
|
||||||
|
|
||||||
|
// Process in a goroutine to avoid blocking the caller
|
||||||
|
go func(remoteOfferAnswer *OfferAnswer) {
|
||||||
|
for {
|
||||||
|
o.fn(remoteOfferAnswer)
|
||||||
|
|
||||||
|
o.mu.Lock()
|
||||||
|
if o.latest == nil {
|
||||||
|
// No more work to do
|
||||||
|
o.running = false
|
||||||
|
o.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remoteOfferAnswer = o.latest
|
||||||
|
// Clear the latest to mark it as being processed
|
||||||
|
o.latest = nil
|
||||||
|
o.mu.Unlock()
|
||||||
|
}
|
||||||
|
}(remoteOfferAnswer)
|
||||||
|
}
|
||||||
39
client/internal/peer/handshaker_listener_test.go
Normal file
39
client/internal/peer/handshaker_listener_test.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_newOfferListener(t *testing.T) {
|
||||||
|
dummyOfferAnswer := &OfferAnswer{}
|
||||||
|
runChan := make(chan struct{}, 10)
|
||||||
|
|
||||||
|
longRunningFn := func(remoteOfferAnswer *OfferAnswer) {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
runChan <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
hl := NewOfferListener(longRunningFn)
|
||||||
|
|
||||||
|
hl.Notify(dummyOfferAnswer)
|
||||||
|
hl.Notify(dummyOfferAnswer)
|
||||||
|
hl.Notify(dummyOfferAnswer)
|
||||||
|
|
||||||
|
// Wait for exactly 2 callbacks
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
select {
|
||||||
|
case <-runChan:
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
t.Fatal("Timeout waiting for callback")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify no additional callbacks happen
|
||||||
|
select {
|
||||||
|
case <-runChan:
|
||||||
|
t.Fatal("Unexpected additional callback")
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
t.Log("Correctly received exactly 2 callbacks")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,9 +21,9 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/internal/ingressgw"
|
"github.com/netbirdio/netbird/client/internal/ingressgw"
|
||||||
"github.com/netbirdio/netbird/client/internal/relay"
|
"github.com/netbirdio/netbird/client/internal/relay"
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
|
"github.com/netbirdio/netbird/route"
|
||||||
"github.com/netbirdio/netbird/shared/management/domain"
|
"github.com/netbirdio/netbird/shared/management/domain"
|
||||||
relayClient "github.com/netbirdio/netbird/shared/relay/client"
|
relayClient "github.com/netbirdio/netbird/shared/relay/client"
|
||||||
"github.com/netbirdio/netbird/route"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const eventQueueSize = 10
|
const eventQueueSize = 10
|
||||||
@@ -201,6 +201,8 @@ type Status struct {
|
|||||||
resolvedDomainsStates map[domain.Domain]ResolvedDomainInfo
|
resolvedDomainsStates map[domain.Domain]ResolvedDomainInfo
|
||||||
lazyConnectionEnabled bool
|
lazyConnectionEnabled bool
|
||||||
|
|
||||||
|
lastDisconnectLog map[string]time.Time
|
||||||
|
|
||||||
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
||||||
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
||||||
// set to true this variable and at the end of the processing we will reset it by the FinishPeerListModifications()
|
// set to true this variable and at the end of the processing we will reset it by the FinishPeerListModifications()
|
||||||
@@ -229,6 +231,7 @@ func NewRecorder(mgmAddress string) *Status {
|
|||||||
notifier: newNotifier(),
|
notifier: newNotifier(),
|
||||||
mgmAddress: mgmAddress,
|
mgmAddress: mgmAddress,
|
||||||
resolvedDomainsStates: map[domain.Domain]ResolvedDomainInfo{},
|
resolvedDomainsStates: map[domain.Domain]ResolvedDomainInfo{},
|
||||||
|
lastDisconnectLog: make(map[string]time.Time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,6 +490,9 @@ func (d *Status) UpdatePeerRelayedStateToDisconnected(receivedState State) error
|
|||||||
|
|
||||||
d.peers[receivedState.PubKey] = peerState
|
d.peers[receivedState.PubKey] = peerState
|
||||||
|
|
||||||
|
// info log about disconnect with impacted routes (throttled)
|
||||||
|
d.logPeerDisconnectIfNeeded(receivedState.PubKey, peerState)
|
||||||
|
|
||||||
if hasConnStatusChanged(oldState, receivedState.ConnStatus) {
|
if hasConnStatusChanged(oldState, receivedState.ConnStatus) {
|
||||||
d.notifyPeerListChanged()
|
d.notifyPeerListChanged()
|
||||||
}
|
}
|
||||||
@@ -519,6 +525,9 @@ func (d *Status) UpdatePeerICEStateToDisconnected(receivedState State) error {
|
|||||||
|
|
||||||
d.peers[receivedState.PubKey] = peerState
|
d.peers[receivedState.PubKey] = peerState
|
||||||
|
|
||||||
|
// info log about disconnect with impacted routes (throttled)
|
||||||
|
d.logPeerDisconnectIfNeeded(receivedState.PubKey, peerState)
|
||||||
|
|
||||||
if hasConnStatusChanged(oldState, receivedState.ConnStatus) {
|
if hasConnStatusChanged(oldState, receivedState.ConnStatus) {
|
||||||
d.notifyPeerListChanged()
|
d.notifyPeerListChanged()
|
||||||
}
|
}
|
||||||
@@ -529,6 +538,49 @@ func (d *Status) UpdatePeerICEStateToDisconnected(receivedState State) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// logPeerDisconnectIfNeeded logs an info message when a routing peer transitions to disconnected
|
||||||
|
// with the number of impacted routes. Throttled to once per peer per 30 seconds.
|
||||||
|
func (d *Status) logPeerDisconnectIfNeeded(pubKey string, state State) {
|
||||||
|
if state.ConnStatus != StatusIdle {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
last, ok := d.lastDisconnectLog[pubKey]
|
||||||
|
if ok && now.Sub(last) < 10*time.Second {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.lastDisconnectLog[pubKey] = now
|
||||||
|
|
||||||
|
routes := state.GetRoutes()
|
||||||
|
numRoutes := len(routes)
|
||||||
|
|
||||||
|
fqdn := state.FQDN
|
||||||
|
if fqdn == "" {
|
||||||
|
fqdn = pubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare a bounded list of impacted routes to avoid huge log lines
|
||||||
|
maxList := 20
|
||||||
|
list := make([]string, 0, maxList)
|
||||||
|
for r := range routes {
|
||||||
|
if len(list) >= maxList {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
list = append(list, r)
|
||||||
|
}
|
||||||
|
more := ""
|
||||||
|
if numRoutes > len(list) {
|
||||||
|
more = ", more=" + fmt.Sprintf("%d", numRoutes-len(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(list) > 0 {
|
||||||
|
log.Warnf("[d] Routing peer disconnected: peer=%s impacted_routes=%d routes=%v%s", fqdn, numRoutes, list, more)
|
||||||
|
} else {
|
||||||
|
log.Warnf("[d] Routing peer disconnected: peer=%s impacted_routes=%d", fqdn, numRoutes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateWireGuardPeerState updates the WireGuard bits of the peer state
|
// UpdateWireGuardPeerState updates the WireGuard bits of the peer state
|
||||||
func (d *Status) UpdateWireGuardPeerState(pubKey string, wgStats configurer.WGStats) error {
|
func (d *Status) UpdateWireGuardPeerState(pubKey string, wgStats configurer.WGStats) error {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ type WGWatcher struct {
|
|||||||
peerKey string
|
peerKey string
|
||||||
stateDump *stateDump
|
stateDump *stateDump
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxCancel context.CancelFunc
|
ctxCancel context.CancelFunc
|
||||||
ctxLock sync.Mutex
|
ctxLock sync.Mutex
|
||||||
|
enabledTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWGWatcher(log *log.Entry, wgIfaceStater WGInterfaceStater, peerKey string, stateDump *stateDump) *WGWatcher {
|
func NewWGWatcher(log *log.Entry, wgIfaceStater WGInterfaceStater, peerKey string, stateDump *stateDump) *WGWatcher {
|
||||||
@@ -48,6 +49,7 @@ func NewWGWatcher(log *log.Entry, wgIfaceStater WGInterfaceStater, peerKey strin
|
|||||||
func (w *WGWatcher) EnableWgWatcher(parentCtx context.Context, onDisconnectedFn func()) {
|
func (w *WGWatcher) EnableWgWatcher(parentCtx context.Context, onDisconnectedFn func()) {
|
||||||
w.log.Debugf("enable WireGuard watcher")
|
w.log.Debugf("enable WireGuard watcher")
|
||||||
w.ctxLock.Lock()
|
w.ctxLock.Lock()
|
||||||
|
w.enabledTime = time.Now()
|
||||||
|
|
||||||
if w.ctx != nil && w.ctx.Err() == nil {
|
if w.ctx != nil && w.ctx.Err() == nil {
|
||||||
w.log.Errorf("WireGuard watcher already enabled")
|
w.log.Errorf("WireGuard watcher already enabled")
|
||||||
@@ -101,6 +103,11 @@ func (w *WGWatcher) periodicHandshakeCheck(ctx context.Context, ctxCancel contex
|
|||||||
onDisconnectedFn()
|
onDisconnectedFn()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if lastHandshake.IsZero() {
|
||||||
|
elapsed := handshake.Sub(w.enabledTime).Seconds()
|
||||||
|
w.log.Infof("first wg handshake detected within: %.2fsec, (%s)", elapsed, handshake)
|
||||||
|
}
|
||||||
|
|
||||||
lastHandshake = *handshake
|
lastHandshake = *handshake
|
||||||
|
|
||||||
resetTime := time.Until(handshake.Add(checkPeriod))
|
resetTime := time.Until(handshake.Add(checkPeriod))
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ func (w *WorkerICE) OnNewOffer(remoteOfferAnswer *OfferAnswer) {
|
|||||||
w.log.Warnf("failed to close ICE agent: %s", err)
|
w.log.Warnf("failed to close ICE agent: %s", err)
|
||||||
}
|
}
|
||||||
w.agent = nil
|
w.agent = nil
|
||||||
// todo consider to switch to Relay connection while establishing a new ICE connection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var preferredCandidateTypes []ice.CandidateType
|
var preferredCandidateTypes []ice.CandidateType
|
||||||
@@ -410,7 +409,10 @@ func (w *WorkerICE) onConnectionStateChange(agent *icemaker.ThreadSafeAgent, dia
|
|||||||
case ice.ConnectionStateConnected:
|
case ice.ConnectionStateConnected:
|
||||||
w.lastKnownState = ice.ConnectionStateConnected
|
w.lastKnownState = ice.ConnectionStateConnected
|
||||||
return
|
return
|
||||||
case ice.ConnectionStateFailed, ice.ConnectionStateDisconnected:
|
case ice.ConnectionStateFailed, ice.ConnectionStateDisconnected, ice.ConnectionStateClosed:
|
||||||
|
// ice.ConnectionStateClosed happens when we recreate the agent. For the P2P to TURN switch important to
|
||||||
|
// notify the conn.onICEStateDisconnected changes to update the current used priority
|
||||||
|
|
||||||
if w.lastKnownState == ice.ConnectionStateConnected {
|
if w.lastKnownState == ice.ConnectionStateConnected {
|
||||||
w.lastKnownState = ice.ConnectionStateDisconnected
|
w.lastKnownState = ice.ConnectionStateDisconnected
|
||||||
w.conn.onICEStateDisconnected()
|
w.conn.onICEStateDisconnected()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func NewNetWithDiscover(iFaceDiscover ExternalIFaceDiscover, disallowList []stri
|
|||||||
if netstack.IsEnabled() {
|
if netstack.IsEnabled() {
|
||||||
n.iFaceDiscover = pionDiscover{}
|
n.iFaceDiscover = pionDiscover{}
|
||||||
} else {
|
} else {
|
||||||
newMobileIFaceDiscover(iFaceDiscover)
|
n.iFaceDiscover = newMobileIFaceDiscover(iFaceDiscover)
|
||||||
}
|
}
|
||||||
return n, n.UpdateInterfaces()
|
return n, n.UpdateInterfaces()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,25 +10,24 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"go.opentelemetry.io/otel"
|
|
||||||
|
|
||||||
"github.com/netbirdio/management-integrations/integrations"
|
|
||||||
"github.com/netbirdio/netbird/management/internals/server/config"
|
|
||||||
"github.com/netbirdio/netbird/management/server/groups"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
||||||
daemonProto "github.com/netbirdio/netbird/client/proto"
|
daemonProto "github.com/netbirdio/netbird/client/proto"
|
||||||
|
"github.com/netbirdio/netbird/management/internals/server/config"
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/groups"
|
||||||
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
||||||
|
"github.com/netbirdio/netbird/management/server/peers"
|
||||||
"github.com/netbirdio/netbird/management/server/permissions"
|
"github.com/netbirdio/netbird/management/server/permissions"
|
||||||
"github.com/netbirdio/netbird/management/server/settings"
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
"github.com/netbirdio/netbird/management/server/store"
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
@@ -294,15 +293,20 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
ia, _ := integrations.NewIntegratedValidator(context.Background(), eventStore)
|
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
t.Cleanup(ctrl.Finish)
|
||||||
|
|
||||||
|
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
||||||
|
peersManager := peers.NewManager(store, permissionsManagerMock)
|
||||||
|
settingsManagerMock := settings.NewMockManager(ctrl)
|
||||||
|
|
||||||
|
ia, _ := integrations.NewIntegratedValidator(context.Background(), peersManager, settingsManagerMock, eventStore)
|
||||||
|
|
||||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
t.Cleanup(ctrl.Finish)
|
|
||||||
settingsMockManager := settings.NewMockManager(ctrl)
|
settingsMockManager := settings.NewMockManager(ctrl)
|
||||||
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
|
||||||
groupsManager := groups.NewManagerMock()
|
groupsManager := groups.NewManagerMock()
|
||||||
|
|
||||||
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
|
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/shared/management/proto"
|
"github.com/netbirdio/netbird/shared/management/proto"
|
||||||
@@ -95,14 +96,6 @@ func (i *Info) SetFlags(
|
|||||||
i.LazyConnectionEnabled = lazyConnectionEnabled
|
i.LazyConnectionEnabled = lazyConnectionEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// StaticInfo is an object that contains machine information that does not change
|
|
||||||
type StaticInfo struct {
|
|
||||||
SystemSerialNumber string
|
|
||||||
SystemProductName string
|
|
||||||
SystemManufacturer string
|
|
||||||
Environment Environment
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context
|
// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context
|
||||||
func extractUserAgent(ctx context.Context) string {
|
func extractUserAgent(ctx context.Context) string {
|
||||||
md, hasMeta := metadata.FromOutgoingContext(ctx)
|
md, hasMeta := metadata.FromOutgoingContext(ctx)
|
||||||
@@ -180,6 +173,7 @@ func isDuplicated(addresses []NetworkAddress, addr NetworkAddress) bool {
|
|||||||
|
|
||||||
// GetInfoWithChecks retrieves and parses the system information with applied checks.
|
// GetInfoWithChecks retrieves and parses the system information with applied checks.
|
||||||
func GetInfoWithChecks(ctx context.Context, checks []*proto.Checks) (*Info, error) {
|
func GetInfoWithChecks(ctx context.Context, checks []*proto.Checks) (*Info, error) {
|
||||||
|
log.Debugf("gathering system information with checks: %d", len(checks))
|
||||||
processCheckPaths := make([]string, 0)
|
processCheckPaths := make([]string, 0)
|
||||||
for _, check := range checks {
|
for _, check := range checks {
|
||||||
processCheckPaths = append(processCheckPaths, check.GetFiles()...)
|
processCheckPaths = append(processCheckPaths, check.GetFiles()...)
|
||||||
@@ -189,16 +183,11 @@ func GetInfoWithChecks(ctx context.Context, checks []*proto.Checks) (*Info, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
log.Debugf("gathering process check information completed")
|
||||||
|
|
||||||
info := GetInfo(ctx)
|
info := GetInfo(ctx)
|
||||||
info.Files = files
|
info.Files = files
|
||||||
|
|
||||||
|
log.Debugf("all system information gathered successfully")
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStaticInfo asynchronously updates static system and platform information
|
|
||||||
func UpdateStaticInfo() {
|
|
||||||
go func() {
|
|
||||||
_ = updateStaticInfo()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ import (
|
|||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UpdateStaticInfoAsync is a no-op on Android as there is no static info to update
|
||||||
|
func UpdateStaticInfoAsync() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
func GetInfo(ctx context.Context) *Info {
|
func GetInfo(ctx context.Context) *Info {
|
||||||
kernel := "android"
|
kernel := "android"
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ import (
|
|||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func UpdateStaticInfoAsync() {
|
||||||
|
go updateStaticInfo()
|
||||||
|
}
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
func GetInfo(ctx context.Context) *Info {
|
func GetInfo(ctx context.Context) *Info {
|
||||||
utsname := unix.Utsname{}
|
utsname := unix.Utsname{}
|
||||||
@@ -41,7 +45,7 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
si := updateStaticInfo()
|
si := getStaticInfo()
|
||||||
if time.Since(start) > 1*time.Second {
|
if time.Since(start) > 1*time.Second {
|
||||||
log.Warnf("updateStaticInfo took %s", time.Since(start))
|
log.Warnf("updateStaticInfo took %s", time.Since(start))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ import (
|
|||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UpdateStaticInfoAsync is a no-op on Android as there is no static info to update
|
||||||
|
func UpdateStaticInfoAsync() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
func GetInfo(ctx context.Context) *Info {
|
func GetInfo(ctx context.Context) *Info {
|
||||||
out := _getInfo()
|
out := _getInfo()
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ import (
|
|||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UpdateStaticInfoAsync is a no-op on Android as there is no static info to update
|
||||||
|
func UpdateStaticInfoAsync() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
func GetInfo(ctx context.Context) *Info {
|
func GetInfo(ctx context.Context) *Info {
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ var (
|
|||||||
getSystemInfo = defaultSysInfoImplementation
|
getSystemInfo = defaultSysInfoImplementation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func UpdateStaticInfoAsync() {
|
||||||
|
go updateStaticInfo()
|
||||||
|
}
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
func GetInfo(ctx context.Context) *Info {
|
func GetInfo(ctx context.Context) *Info {
|
||||||
info := _getInfo()
|
info := _getInfo()
|
||||||
@@ -48,7 +52,7 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
si := updateStaticInfo()
|
si := getStaticInfo()
|
||||||
if time.Since(start) > 1*time.Second {
|
if time.Since(start) > 1*time.Second {
|
||||||
log.Warnf("updateStaticInfo took %s", time.Since(start))
|
log.Warnf("updateStaticInfo took %s", time.Since(start))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,187 +2,51 @@ package system
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/yusufpapurcu/wmi"
|
|
||||||
"golang.org/x/sys/windows/registry"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Win32_OperatingSystem struct {
|
func UpdateStaticInfoAsync() {
|
||||||
Caption string
|
go updateStaticInfo()
|
||||||
}
|
|
||||||
|
|
||||||
type Win32_ComputerSystem struct {
|
|
||||||
Manufacturer string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Win32_ComputerSystemProduct struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Win32_BIOS struct {
|
|
||||||
SerialNumber string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
func GetInfo(ctx context.Context) *Info {
|
func GetInfo(ctx context.Context) *Info {
|
||||||
osName, osVersion := getOSNameAndVersion()
|
|
||||||
buildVersion := getBuildVersion()
|
|
||||||
|
|
||||||
addrs, err := networkAddresses()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to discover network addresses: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
si := updateStaticInfo()
|
si := getStaticInfo()
|
||||||
if time.Since(start) > 1*time.Second {
|
if time.Since(start) > 1*time.Second {
|
||||||
log.Warnf("updateStaticInfo took %s", time.Since(start))
|
log.Warnf("updateStaticInfo took %s", time.Since(start))
|
||||||
}
|
}
|
||||||
|
|
||||||
gio := &Info{
|
gio := &Info{
|
||||||
Kernel: "windows",
|
Kernel: "windows",
|
||||||
OSVersion: osVersion,
|
OSVersion: si.OSVersion,
|
||||||
Platform: "unknown",
|
Platform: "unknown",
|
||||||
OS: osName,
|
OS: si.OSName,
|
||||||
GoOS: runtime.GOOS,
|
GoOS: runtime.GOOS,
|
||||||
CPUs: runtime.NumCPU(),
|
CPUs: runtime.NumCPU(),
|
||||||
KernelVersion: buildVersion,
|
KernelVersion: si.BuildVersion,
|
||||||
NetworkAddresses: addrs,
|
|
||||||
SystemSerialNumber: si.SystemSerialNumber,
|
SystemSerialNumber: si.SystemSerialNumber,
|
||||||
SystemProductName: si.SystemProductName,
|
SystemProductName: si.SystemProductName,
|
||||||
SystemManufacturer: si.SystemManufacturer,
|
SystemManufacturer: si.SystemManufacturer,
|
||||||
Environment: si.Environment,
|
Environment: si.Environment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addrs, err := networkAddresses()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to discover network addresses: %s", err)
|
||||||
|
} else {
|
||||||
|
gio.NetworkAddresses = addrs
|
||||||
|
}
|
||||||
|
|
||||||
systemHostname, _ := os.Hostname()
|
systemHostname, _ := os.Hostname()
|
||||||
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
||||||
gio.NetbirdVersion = version.NetbirdVersion()
|
gio.NetbirdVersion = version.NetbirdVersion()
|
||||||
gio.UIVersion = extractUserAgent(ctx)
|
gio.UIVersion = extractUserAgent(ctx)
|
||||||
|
|
||||||
return gio
|
return gio
|
||||||
}
|
}
|
||||||
|
|
||||||
func sysInfo() (serialNumber string, productName string, manufacturer string) {
|
|
||||||
var err error
|
|
||||||
serialNumber, err = sysNumber()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to get system serial number: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
productName, err = sysProductName()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to get system product name: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
manufacturer, err = sysManufacturer()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to get system manufacturer: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return serialNumber, productName, manufacturer
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOSNameAndVersion() (string, string) {
|
|
||||||
var dst []Win32_OperatingSystem
|
|
||||||
query := wmi.CreateQuery(&dst, "")
|
|
||||||
err := wmi.Query(query, &dst)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return "Windows", getBuildVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(dst) == 0 {
|
|
||||||
return "Windows", getBuildVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
split := strings.Split(dst[0].Caption, " ")
|
|
||||||
|
|
||||||
if len(split) <= 3 {
|
|
||||||
return "Windows", getBuildVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
name := split[1]
|
|
||||||
version := split[2]
|
|
||||||
if split[2] == "Server" {
|
|
||||||
name = fmt.Sprintf("%s %s", split[1], split[2])
|
|
||||||
version = split[3]
|
|
||||||
}
|
|
||||||
|
|
||||||
return name, version
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBuildVersion() string {
|
|
||||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return "0.0.0.0"
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
deferErr := k.Close()
|
|
||||||
if deferErr != nil {
|
|
||||||
log.Error(deferErr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
major, _, err := k.GetIntegerValue("CurrentMajorVersionNumber")
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
minor, _, err := k.GetIntegerValue("CurrentMinorVersionNumber")
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
build, _, err := k.GetStringValue("CurrentBuildNumber")
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
// Update Build Revision
|
|
||||||
ubr, _, err := k.GetIntegerValue("UBR")
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
ver := fmt.Sprintf("%d.%d.%s.%d", major, minor, build, ubr)
|
|
||||||
return ver
|
|
||||||
}
|
|
||||||
|
|
||||||
func sysNumber() (string, error) {
|
|
||||||
var dst []Win32_BIOS
|
|
||||||
query := wmi.CreateQuery(&dst, "")
|
|
||||||
err := wmi.Query(query, &dst)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return dst[0].SerialNumber, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func sysProductName() (string, error) {
|
|
||||||
var dst []Win32_ComputerSystemProduct
|
|
||||||
query := wmi.CreateQuery(&dst, "")
|
|
||||||
err := wmi.Query(query, &dst)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// `ComputerSystemProduct` could be empty on some virtualized systems
|
|
||||||
if len(dst) < 1 {
|
|
||||||
return "unknown", nil
|
|
||||||
}
|
|
||||||
return dst[0].Name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func sysManufacturer() (string, error) {
|
|
||||||
var dst []Win32_ComputerSystem
|
|
||||||
query := wmi.CreateQuery(&dst, "")
|
|
||||||
err := wmi.Query(query, &dst)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return dst[0].Manufacturer, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,12 +3,7 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/system/detect_cloud"
|
|
||||||
"github.com/netbirdio/netbird/client/system/detect_platform"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -16,25 +11,26 @@ var (
|
|||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
func updateStaticInfo() StaticInfo {
|
// StaticInfo is an object that contains machine information that does not change
|
||||||
|
type StaticInfo struct {
|
||||||
|
SystemSerialNumber string
|
||||||
|
SystemProductName string
|
||||||
|
SystemManufacturer string
|
||||||
|
Environment Environment
|
||||||
|
|
||||||
|
// Windows specific fields
|
||||||
|
OSName string
|
||||||
|
OSVersion string
|
||||||
|
BuildVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateStaticInfo() {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
staticInfo = newStaticInfo()
|
||||||
defer cancel()
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
wg.Add(3)
|
|
||||||
go func() {
|
|
||||||
staticInfo.SystemSerialNumber, staticInfo.SystemProductName, staticInfo.SystemManufacturer = sysInfo()
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
staticInfo.Environment.Cloud = detect_cloud.Detect(ctx)
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
staticInfo.Environment.Platform = detect_platform.Detect(ctx)
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStaticInfo() StaticInfo {
|
||||||
|
updateStaticInfo()
|
||||||
return staticInfo
|
return staticInfo
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
//go:build android || freebsd || ios
|
|
||||||
|
|
||||||
package system
|
|
||||||
|
|
||||||
// updateStaticInfo returns an empty implementation for unsupported platforms
|
|
||||||
func updateStaticInfo() StaticInfo {
|
|
||||||
return StaticInfo{}
|
|
||||||
}
|
|
||||||
35
client/system/static_info_update.go
Normal file
35
client/system/static_info_update.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
//go:build (linux && !android) || (darwin && !ios)
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_cloud"
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_platform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newStaticInfo() StaticInfo {
|
||||||
|
si := StaticInfo{}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(3)
|
||||||
|
go func() {
|
||||||
|
si.SystemSerialNumber, si.SystemProductName, si.SystemManufacturer = sysInfo()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
si.Environment.Cloud = detect_cloud.Detect(ctx)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
si.Environment.Platform = detect_platform.Detect(ctx)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
return si
|
||||||
|
}
|
||||||
184
client/system/static_info_update_windows.go
Normal file
184
client/system/static_info_update_windows.go
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/yusufpapurcu/wmi"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_cloud"
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_platform"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Win32_OperatingSystem struct {
|
||||||
|
Caption string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Win32_ComputerSystem struct {
|
||||||
|
Manufacturer string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Win32_ComputerSystemProduct struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Win32_BIOS struct {
|
||||||
|
SerialNumber string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStaticInfo() StaticInfo {
|
||||||
|
si := StaticInfo{}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
si.SystemSerialNumber, si.SystemProductName, si.SystemManufacturer = sysInfo()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
si.Environment.Cloud = detect_cloud.Detect(ctx)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
si.Environment.Platform = detect_platform.Detect(ctx)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
si.OSName, si.OSVersion = getOSNameAndVersion()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
si.BuildVersion = getBuildVersion()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
return si
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysInfo() (serialNumber string, productName string, manufacturer string) {
|
||||||
|
var err error
|
||||||
|
serialNumber, err = sysNumber()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to get system serial number: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
productName, err = sysProductName()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to get system product name: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manufacturer, err = sysManufacturer()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to get system manufacturer: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return serialNumber, productName, manufacturer
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysNumber() (string, error) {
|
||||||
|
var dst []Win32_BIOS
|
||||||
|
query := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(query, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return dst[0].SerialNumber, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysProductName() (string, error) {
|
||||||
|
var dst []Win32_ComputerSystemProduct
|
||||||
|
query := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(query, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// `ComputerSystemProduct` could be empty on some virtualized systems
|
||||||
|
if len(dst) < 1 {
|
||||||
|
return "unknown", nil
|
||||||
|
}
|
||||||
|
return dst[0].Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysManufacturer() (string, error) {
|
||||||
|
var dst []Win32_ComputerSystem
|
||||||
|
query := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(query, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return dst[0].Manufacturer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOSNameAndVersion() (string, string) {
|
||||||
|
var dst []Win32_OperatingSystem
|
||||||
|
query := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(query, &dst)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return "Windows", getBuildVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dst) == 0 {
|
||||||
|
return "Windows", getBuildVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
split := strings.Split(dst[0].Caption, " ")
|
||||||
|
|
||||||
|
if len(split) <= 3 {
|
||||||
|
return "Windows", getBuildVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
name := split[1]
|
||||||
|
version := split[2]
|
||||||
|
if split[2] == "Server" {
|
||||||
|
name = fmt.Sprintf("%s %s", split[1], split[2])
|
||||||
|
version = split[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, version
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBuildVersion() string {
|
||||||
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return "0.0.0.0"
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
deferErr := k.Close()
|
||||||
|
if deferErr != nil {
|
||||||
|
log.Error(deferErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
major, _, err := k.GetIntegerValue("CurrentMajorVersionNumber")
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
minor, _, err := k.GetIntegerValue("CurrentMinorVersionNumber")
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
build, _, err := k.GetStringValue("CurrentBuildNumber")
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
// Update Build Revision
|
||||||
|
ubr, _, err := k.GetIntegerValue("UBR")
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
ver := fmt.Sprintf("%d.%d.%s.%d", major, minor, build, ubr)
|
||||||
|
return ver
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@@ -62,7 +62,7 @@ require (
|
|||||||
github.com/miekg/dns v1.1.59
|
github.com/miekg/dns v1.1.59
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/nadoo/ipset v0.5.0
|
github.com/nadoo/ipset v0.5.0
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20250820151658-9ee1b34f4190
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20250906095204-f87a07690ba0
|
||||||
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45
|
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45
|
||||||
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0
|
github.com/oschwald/maxminddb-golang v1.12.0
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -503,8 +503,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/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ=
|
||||||
github.com/netbirdio/ice/v4 v4.0.0-20250827161942-426799a23107 h1:ZJwhKexMlK15B/Ld+1T8VYE2Mt1lk1kf2DlXr46EHcw=
|
github.com/netbirdio/ice/v4 v4.0.0-20250827161942-426799a23107 h1:ZJwhKexMlK15B/Ld+1T8VYE2Mt1lk1kf2DlXr46EHcw=
|
||||||
github.com/netbirdio/ice/v4 v4.0.0-20250827161942-426799a23107/go.mod h1:ZSIbPdBn5hePO8CpF1PekH2SfpTxg1PDhEwtbqZS7R8=
|
github.com/netbirdio/ice/v4 v4.0.0-20250827161942-426799a23107/go.mod h1:ZSIbPdBn5hePO8CpF1PekH2SfpTxg1PDhEwtbqZS7R8=
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20250820151658-9ee1b34f4190 h1:/ZbExdcDwRq6XgTpTf5I1DPqnC3eInEf0fcmkqR8eSg=
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20250906095204-f87a07690ba0 h1:9BUqQHPVOGr0edk8EifUBUfTr2Ob0ypAPxtasUApBxQ=
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20250820151658-9ee1b34f4190/go.mod h1:v0nUbbHbuQnqR7yKIYnKzsLBCswLtp2JctmKYmGgVhc=
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20250906095204-f87a07690ba0/go.mod h1:v0nUbbHbuQnqR7yKIYnKzsLBCswLtp2JctmKYmGgVhc=
|
||||||
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8=
|
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/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||||
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45 h1:ujgviVYmx243Ksy7NdSwrdGPSRNE3pb8kEDSpH0QuAQ=
|
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45 h1:ujgviVYmx243Ksy7NdSwrdGPSRNE3pb8kEDSpH0QuAQ=
|
||||||
|
|||||||
@@ -20,7 +20,11 @@ func (s *BaseServer) PeersUpdateManager() *server.PeersUpdateManager {
|
|||||||
|
|
||||||
func (s *BaseServer) IntegratedValidator() integrated_validator.IntegratedValidator {
|
func (s *BaseServer) IntegratedValidator() integrated_validator.IntegratedValidator {
|
||||||
return Create(s, func() integrated_validator.IntegratedValidator {
|
return Create(s, func() integrated_validator.IntegratedValidator {
|
||||||
integratedPeerValidator, err := integrations.NewIntegratedValidator(context.Background(), s.EventStore())
|
integratedPeerValidator, err := integrations.NewIntegratedValidator(
|
||||||
|
context.Background(),
|
||||||
|
s.PeersManager(),
|
||||||
|
s.SettingsManager(),
|
||||||
|
s.EventStore())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to create integrated peer validator: %v", err)
|
log.Errorf("failed to create integrated peer validator: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1714,7 +1714,9 @@ func (am *DefaultAccountManager) onPeersInvalidated(ctx context.Context, account
|
|||||||
log.WithContext(ctx).Errorf("failed to get invalidated peer %s for account %s: %v", peerID, accountID, err)
|
log.WithContext(ctx).Errorf("failed to get invalidated peer %s for account %s: %v", peerID, accountID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
peers = append(peers, peer)
|
if peer.UserID != "" {
|
||||||
|
peers = append(peers, peer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(peers) > 0 {
|
if len(peers) > 0 {
|
||||||
err := am.expireAndUpdatePeers(ctx, accountID, peers)
|
err := am.expireAndUpdatePeers(ctx, accountID, peers)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Manager interface {
|
|||||||
GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error)
|
GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error)
|
||||||
GetPeerAccountID(ctx context.Context, peerID string) (string, error)
|
GetPeerAccountID(ctx context.Context, peerID string) (string, error)
|
||||||
GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error)
|
GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error)
|
||||||
|
GetPeersByGroupIDs(ctx context.Context, accountID string, groupsIDs []string) ([]*peer.Peer, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type managerImpl struct {
|
type managerImpl struct {
|
||||||
@@ -61,3 +62,7 @@ func (m *managerImpl) GetAllPeers(ctx context.Context, accountID, userID string)
|
|||||||
func (m *managerImpl) GetPeerAccountID(ctx context.Context, peerID string) (string, error) {
|
func (m *managerImpl) GetPeerAccountID(ctx context.Context, peerID string) (string, error) {
|
||||||
return m.store.GetAccountIDByPeerID(ctx, store.LockingStrengthNone, peerID)
|
return m.store.GetAccountIDByPeerID(ctx, store.LockingStrengthNone, peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *managerImpl) GetPeersByGroupIDs(ctx context.Context, accountID string, groupsIDs []string) ([]*peer.Peer, error) {
|
||||||
|
return m.store.GetPeersByGroupIDs(ctx, accountID, groupsIDs)
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,3 +79,18 @@ func (mr *MockManagerMockRecorder) GetPeerAccountID(ctx, peerID interface{}) *go
|
|||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerAccountID", reflect.TypeOf((*MockManager)(nil).GetPeerAccountID), ctx, peerID)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerAccountID", reflect.TypeOf((*MockManager)(nil).GetPeerAccountID), ctx, peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPeersByGroupIDs mocks base method.
|
||||||
|
func (m *MockManager) GetPeersByGroupIDs(ctx context.Context, accountID string, groupsIDs []string) ([]*peer.Peer, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetPeersByGroupIDs", ctx, accountID, groupsIDs)
|
||||||
|
ret0, _ := ret[0].([]*peer.Peer)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeersByGroupIDs indicates an expected call of GetPeersByGroupIDs.
|
||||||
|
func (mr *MockManagerMockRecorder) GetPeersByGroupIDs(ctx, accountID, groupsIDs interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeersByGroupIDs", reflect.TypeOf((*MockManager)(nil).GetPeersByGroupIDs), ctx, accountID, groupsIDs)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2847,3 +2847,22 @@ func (s *SqlStore) UpdateAccountNetwork(ctx context.Context, accountID string, i
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SqlStore) GetPeersByGroupIDs(ctx context.Context, accountID string, groupIDs []string) ([]*nbpeer.Peer, error) {
|
||||||
|
if len(groupIDs) == 0 {
|
||||||
|
return []*nbpeer.Peer{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var peers []*nbpeer.Peer
|
||||||
|
peerIDsSubquery := s.db.Model(&types.GroupPeer{}).
|
||||||
|
Select("DISTINCT peer_id").
|
||||||
|
Where("account_id = ? AND group_id IN ?", accountID, groupIDs)
|
||||||
|
|
||||||
|
result := s.db.Where("id IN (?)", peerIDsSubquery).Find(&peers)
|
||||||
|
if result.Error != nil {
|
||||||
|
log.WithContext(ctx).Errorf("failed to get peers by group IDs: %s", result.Error)
|
||||||
|
return nil, status.Errorf(status.Internal, "failed to get peers by group IDs")
|
||||||
|
}
|
||||||
|
|
||||||
|
return peers, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3607,3 +3607,113 @@ func intToIPv4(n uint32) net.IP {
|
|||||||
binary.BigEndian.PutUint32(ip, n)
|
binary.BigEndian.PutUint32(ip, n)
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSqlStore_GetPeersByGroupIDs(t *testing.T) {
|
||||||
|
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||||
|
|
||||||
|
group1ID := "test-group-1"
|
||||||
|
group2ID := "test-group-2"
|
||||||
|
emptyGroupID := "empty-group"
|
||||||
|
|
||||||
|
peer1 := "cfefqs706sqkneg59g4g"
|
||||||
|
peer2 := "cfeg6sf06sqkneg59g50"
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
groupIDs []string
|
||||||
|
expectedPeers []string
|
||||||
|
expectedCount int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "retrieve peers from single group with multiple peers",
|
||||||
|
groupIDs: []string{group1ID},
|
||||||
|
expectedPeers: []string{peer1, peer2},
|
||||||
|
expectedCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "retrieve peers from single group with one peer",
|
||||||
|
groupIDs: []string{group2ID},
|
||||||
|
expectedPeers: []string{peer1},
|
||||||
|
expectedCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "retrieve peers from multiple groups (with overlap)",
|
||||||
|
groupIDs: []string{group1ID, group2ID},
|
||||||
|
expectedPeers: []string{peer1, peer2}, // should deduplicate
|
||||||
|
expectedCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "retrieve peers from existing 'All' group",
|
||||||
|
groupIDs: []string{"cfefqs706sqkneg59g3g"}, // All group from test data
|
||||||
|
expectedPeers: []string{peer1, peer2},
|
||||||
|
expectedCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "retrieve peers from empty group",
|
||||||
|
groupIDs: []string{emptyGroupID},
|
||||||
|
expectedPeers: []string{},
|
||||||
|
expectedCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "retrieve peers from non-existing group",
|
||||||
|
groupIDs: []string{"non-existing-group"},
|
||||||
|
expectedPeers: []string{},
|
||||||
|
expectedCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty group IDs list",
|
||||||
|
groupIDs: []string{},
|
||||||
|
expectedPeers: []string{},
|
||||||
|
expectedCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mix of existing and non-existing groups",
|
||||||
|
groupIDs: []string{group1ID, "non-existing-group"},
|
||||||
|
expectedPeers: []string{peer1, peer2},
|
||||||
|
expectedCount: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/store_policy_migrate.sql", t.TempDir())
|
||||||
|
t.Cleanup(cleanup)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
groups := []*types.Group{
|
||||||
|
{
|
||||||
|
ID: group1ID,
|
||||||
|
AccountID: accountID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: group2ID,
|
||||||
|
AccountID: accountID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, store.CreateGroups(ctx, accountID, groups))
|
||||||
|
|
||||||
|
require.NoError(t, store.AddPeerToGroup(ctx, accountID, peer1, group1ID))
|
||||||
|
require.NoError(t, store.AddPeerToGroup(ctx, accountID, peer2, group1ID))
|
||||||
|
require.NoError(t, store.AddPeerToGroup(ctx, accountID, peer1, group2ID))
|
||||||
|
|
||||||
|
peers, err := store.GetPeersByGroupIDs(ctx, accountID, tt.groupIDs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, peers, tt.expectedCount)
|
||||||
|
|
||||||
|
if tt.expectedCount > 0 {
|
||||||
|
actualPeerIDs := make([]string, len(peers))
|
||||||
|
for i, peer := range peers {
|
||||||
|
actualPeerIDs[i] = peer.ID
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, tt.expectedPeers, actualPeerIDs)
|
||||||
|
|
||||||
|
// Verify all returned peers belong to the correct account
|
||||||
|
for _, peer := range peers {
|
||||||
|
assert.Equal(t, accountID, peer.AccountID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ type Store interface {
|
|||||||
GetUserPeers(ctx context.Context, lockStrength LockingStrength, accountID, userID 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)
|
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)
|
GetPeersByIDs(ctx context.Context, lockStrength LockingStrength, accountID string, peerIDs []string) (map[string]*nbpeer.Peer, error)
|
||||||
|
GetPeersByGroupIDs(ctx context.Context, accountID string, groupIDs []string) ([]*nbpeer.Peer, error)
|
||||||
GetAccountPeersWithExpiration(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error)
|
GetAccountPeersWithExpiration(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error)
|
||||||
GetAccountPeersWithInactivity(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error)
|
GetAccountPeersWithInactivity(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error)
|
||||||
GetAllEphemeralPeers(ctx context.Context, lockStrength LockingStrength) ([]*nbpeer.Peer, error)
|
GetAllEphemeralPeers(ctx context.Context, lockStrength LockingStrength) ([]*nbpeer.Peer, error)
|
||||||
|
|||||||
@@ -942,6 +942,11 @@ func (am *DefaultAccountManager) expireAndUpdatePeers(ctx context.Context, accou
|
|||||||
// nolint:staticcheck
|
// nolint:staticcheck
|
||||||
ctx = context.WithValue(ctx, nbContext.PeerIDKey, peer.Key)
|
ctx = context.WithValue(ctx, nbContext.PeerIDKey, peer.Key)
|
||||||
|
|
||||||
|
if peer.UserID == "" {
|
||||||
|
// we do not want to expire peers that are added via setup key
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if peer.Status.LoginExpired {
|
if peer.Status.LoginExpired {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,36 +130,6 @@ repo_gpgcheck=1
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
install_aur_package() {
|
|
||||||
INSTALL_PKGS="git base-devel go"
|
|
||||||
REMOVE_PKGS=""
|
|
||||||
|
|
||||||
# Check if dependencies are installed
|
|
||||||
for PKG in $INSTALL_PKGS; do
|
|
||||||
if ! pacman -Q "$PKG" > /dev/null 2>&1; then
|
|
||||||
# Install missing package(s)
|
|
||||||
${SUDO} pacman -S "$PKG" --noconfirm
|
|
||||||
|
|
||||||
# Add installed package for clean up later
|
|
||||||
REMOVE_PKGS="$REMOVE_PKGS $PKG"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Build package from AUR
|
|
||||||
cd /tmp && git clone https://aur.archlinux.org/netbird.git
|
|
||||||
cd netbird && makepkg -sri --noconfirm
|
|
||||||
|
|
||||||
if ! $SKIP_UI_APP; then
|
|
||||||
cd /tmp && git clone https://aur.archlinux.org/netbird-ui.git
|
|
||||||
cd netbird-ui && makepkg -sri --noconfirm
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$REMOVE_PKGS" ]; then
|
|
||||||
# Clean up the installed packages
|
|
||||||
${SUDO} pacman -Rs "$REMOVE_PKGS" --noconfirm
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
prepare_tun_module() {
|
prepare_tun_module() {
|
||||||
# Create the necessary file structure for /dev/net/tun
|
# Create the necessary file structure for /dev/net/tun
|
||||||
if [ ! -c /dev/net/tun ]; then
|
if [ ! -c /dev/net/tun ]; then
|
||||||
@@ -276,12 +246,9 @@ install_netbird() {
|
|||||||
if ! $SKIP_UI_APP; then
|
if ! $SKIP_UI_APP; then
|
||||||
${SUDO} rpm-ostree -y install netbird-ui
|
${SUDO} rpm-ostree -y install netbird-ui
|
||||||
fi
|
fi
|
||||||
;;
|
# ensure the service is started after install
|
||||||
pacman)
|
${SUDO} netbird service install || true
|
||||||
${SUDO} pacman -Syy
|
${SUDO} netbird service start || true
|
||||||
install_aur_package
|
|
||||||
# in-line with the docs at https://wiki.archlinux.org/title/Netbird
|
|
||||||
${SUDO} systemctl enable --now netbird@main.service
|
|
||||||
;;
|
;;
|
||||||
pkg)
|
pkg)
|
||||||
# Check if the package is already installed
|
# Check if the package is already installed
|
||||||
@@ -458,11 +425,7 @@ if type uname >/dev/null 2>&1; then
|
|||||||
elif [ -x "$(command -v yum)" ]; then
|
elif [ -x "$(command -v yum)" ]; then
|
||||||
PACKAGE_MANAGER="yum"
|
PACKAGE_MANAGER="yum"
|
||||||
echo "The installation will be performed using yum package manager"
|
echo "The installation will be performed using yum package manager"
|
||||||
elif [ -x "$(command -v pacman)" ]; then
|
|
||||||
PACKAGE_MANAGER="pacman"
|
|
||||||
echo "The installation will be performed using pacman package manager"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
echo "Unable to determine OS type from /etc/os-release"
|
echo "Unable to determine OS type from /etc/os-release"
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -9,34 +9,30 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/system"
|
|
||||||
"github.com/netbirdio/netbird/management/internals/server/config"
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
"github.com/netbirdio/netbird/management/server/groups"
|
|
||||||
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
|
||||||
"github.com/netbirdio/netbird/management/server/permissions"
|
|
||||||
"github.com/netbirdio/netbird/management/server/settings"
|
|
||||||
"github.com/netbirdio/netbird/management/server/store"
|
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
|
||||||
"github.com/netbirdio/netbird/management/server/types"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/netbirdio/management-integrations/integrations"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/encryption"
|
|
||||||
mgmt "github.com/netbirdio/netbird/management/server"
|
|
||||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
|
||||||
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
|
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
|
"github.com/netbirdio/netbird/client/system"
|
||||||
|
"github.com/netbirdio/netbird/encryption"
|
||||||
|
"github.com/netbirdio/netbird/management/internals/server/config"
|
||||||
|
mgmt "github.com/netbirdio/netbird/management/server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/groups"
|
||||||
|
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
||||||
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/peers"
|
||||||
|
"github.com/netbirdio/netbird/management/server/permissions"
|
||||||
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -72,13 +68,31 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
|
|||||||
|
|
||||||
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
|
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
|
||||||
eventStore := &activity.InMemoryEventStore{}
|
eventStore := &activity.InMemoryEventStore{}
|
||||||
ia, _ := integrations.NewIntegratedValidator(context.Background(), eventStore)
|
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
t.Cleanup(ctrl.Finish)
|
||||||
|
|
||||||
|
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
||||||
|
permissionsManagerMock.
|
||||||
|
EXPECT().
|
||||||
|
ValidateUserPermissions(
|
||||||
|
gomock.Any(),
|
||||||
|
gomock.Any(),
|
||||||
|
gomock.Any(),
|
||||||
|
gomock.Any(),
|
||||||
|
gomock.Any(),
|
||||||
|
).
|
||||||
|
Return(true, nil).
|
||||||
|
AnyTimes()
|
||||||
|
|
||||||
|
peersManger := peers.NewManager(store, permissionsManagerMock)
|
||||||
|
settingsManagerMock := settings.NewMockManager(ctrl)
|
||||||
|
|
||||||
|
ia, _ := integrations.NewIntegratedValidator(context.Background(), peersManger, settingsManagerMock, eventStore)
|
||||||
|
|
||||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
t.Cleanup(ctrl.Finish)
|
|
||||||
settingsMockManager := settings.NewMockManager(ctrl)
|
settingsMockManager := settings.NewMockManager(ctrl)
|
||||||
settingsMockManager.
|
settingsMockManager.
|
||||||
EXPECT().
|
EXPECT().
|
||||||
@@ -95,19 +109,6 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
|
|||||||
Return(&types.ExtraSettings{}, nil).
|
Return(&types.ExtraSettings{}, nil).
|
||||||
AnyTimes()
|
AnyTimes()
|
||||||
|
|
||||||
permissionsManagerMock := permissions.NewMockManager(ctrl)
|
|
||||||
permissionsManagerMock.
|
|
||||||
EXPECT().
|
|
||||||
ValidateUserPermissions(
|
|
||||||
gomock.Any(),
|
|
||||||
gomock.Any(),
|
|
||||||
gomock.Any(),
|
|
||||||
gomock.Any(),
|
|
||||||
gomock.Any(),
|
|
||||||
).
|
|
||||||
Return(true, nil).
|
|
||||||
AnyTimes()
|
|
||||||
|
|
||||||
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
|
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/formatter"
|
"github.com/netbirdio/netbird/formatter"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultLogSize = 15
|
const defaultLogSize = 100
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LogConsole = "console"
|
LogConsole = "console"
|
||||||
|
|||||||
Reference in New Issue
Block a user