Compare commits

...

35 Commits

Author SHA1 Message Date
Pascal Fischer
5b9378e6cb add creating log dir to macOS installer scripts 2023-05-31 19:31:37 +02:00
Maycon Santos
7f454f9c00 Add retry to sending signal message (#906)
Increased the default send timeout from 2 to 5

Added a max of 4 retries
 with an increased timeout after the second attempt

using the grpc client context and
checking the error value for canceled context
2023-05-26 17:55:37 +02:00
pascal-fischer
d2db6bd03e Merge pull request #899 from netbirdio/feature/create_macos_pkg_on_release
Adding static files for pkg creation for Mac
2023-05-26 17:48:08 +02:00
pascal-fischer
deeff277f4 Merge pull request #907 from netbirdio/chore/remove_drift_in_openapi_and_docs
Remove drift between docs and openapi
2023-05-26 17:33:33 +02:00
Maycon Santos
b6105e9d7c Use backoff.retry to check if upstreams are responsive (#901)
Retry, in an exponential interval, querying the upstream servers until it gets a positive response
2023-05-26 17:13:59 +02:00
Pascal Fischer
2808647be7 upgrade sign pipeline version 2023-05-26 17:06:47 +02:00
Pascal Fischer
7bdb0dd358 merge openapi with version from docs repo 2023-05-26 15:32:52 +02:00
Pascal Fischer
8124a273fb fix log writing 2023-05-26 13:56:01 +02:00
Pascal Fischer
5d459cf118 remove requirements.plist 2023-05-26 13:10:01 +02:00
Pascal Fischer
489be203fc revert logs writing 2023-05-26 13:07:14 +02:00
Pascal Fischer
4eec29a639 revert log writing 2023-05-25 21:22:26 +02:00
Pascal Fischer
b3027603df update postinstall 2023-05-25 21:14:44 +02:00
Pascal Fischer
4026efcc08 revert requirements.plist 2023-05-25 21:02:49 +02:00
Pascal Fischer
fb3fbc17f2 update requirements.plist 2023-05-25 15:13:38 +02:00
Pascal Fischer
76004bd537 update requirements.plist 2023-05-25 14:54:48 +02:00
Pascal Fischer
4e69af6caa also write error messages 2023-05-25 14:40:32 +02:00
Zoltan Papp
f237e8bd30 Windows MTU fix and wg/win version update (#896)
- wireguard/windows version update to 0.5.3
- follow up forked wireguard-go MTU related changes
- fix MTU settings on Windows

---------

Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2023-05-25 14:16:24 +02:00
Pascal Fischer
98eb2d4587 update log path 2023-05-25 12:22:13 +02:00
Pascal Fischer
ac0e40da7e add scripts for pkg creation for mac 2023-05-23 18:15:05 +02:00
Maycon Santos
a91297d3a4 Check if the cancel function was set before using it (#893)
in some cases an IDP device flow expiration time might be shorter than 90s
we should check if the cancel context was set before using it

We will need a follow-up to identify and document the IDP with lower defaults.

fixes #890
2023-05-23 17:54:47 +02:00
Misha Bragin
f66574b094 Count only successful HTTP request durations (#886) 2023-05-22 16:26:36 +02:00
Misha Bragin
48265b32f3 Measure write requests separately from read requests (#880) 2023-05-19 16:56:15 +02:00
Misha Bragin
03a42de5a0 Add telemetry to measure app durations (#878) 2023-05-19 11:42:25 +02:00
Misha Bragin
8b78209ae5 Clarify XORMapped panic case (#877) 2023-05-18 19:47:36 +02:00
Zoltan Papp
8a8c4bdddd Fix issue 872 (#873)
Read and check ip_forward from proc before write
2023-05-18 19:31:54 +02:00
Maycon Santos
48a8b52740 Avoid storing account if no peer meta or expiration change (#875)
* Avoid storing account if no peer meta or expiration change

* remove extra log

* Update management/server/peer.go

Co-authored-by: Misha Bragin <bangvalo@gmail.com>

* Clarify why we need to skip account update

---------

Co-authored-by: Misha Bragin <bangvalo@gmail.com>
2023-05-18 19:31:35 +02:00
Misha Bragin
3876cb26f4 Fix panic when getting XORMapped addr (#874) 2023-05-18 18:50:46 +02:00
Misha Bragin
6e9f7531f5 Track user block/unblock activity event (#865) 2023-05-17 09:54:20 +02:00
Maycon Santos
db69a0cf9d Prevent setting primary resolver if using custom DNS port (#861)
Most host managers doesn't support using custom DNS ports.
We are now disabling setting it up to avoid unwanted results
2023-05-17 00:03:26 +02:00
pascal-fischer
4c5b85d80b Merge pull request #863 from netbirdio/fix/base62_dependency
Remove dependency to base62 package
2023-05-16 13:36:08 +02:00
Pascal Fischer
873abc43bf move into separate package 2023-05-16 12:57:56 +02:00
Pascal Fischer
2fef52b856 remove dependency to external base62 package and create own methods in utils 2023-05-16 12:44:26 +02:00
Ovidiu Ionescu
a3ee45b79e Add mipsle build to enable netbird for devices such as EdgeRouter X (#842)
Add mipsle build and split build for mipsle and mips archs.

Removed yum and debian packages for these archs.
2023-05-14 12:06:29 +02:00
pascal-fischer
c2770c7bf9 Merge pull request #851 from bcmmbaga/bug/oidc-config
Resolve issue with AuthIssuer URL assignment in auth0
2023-05-12 17:25:41 +02:00
Bethuel
2570363861 fix assign correct issuer url to auth0 AuthIssuer 2023-05-12 18:07:11 +03:00
53 changed files with 1075 additions and 379 deletions

View File

@@ -9,7 +9,7 @@ on:
pull_request: pull_request:
env: env:
SIGN_PIPE_VER: "v0.0.6" SIGN_PIPE_VER: "v0.0.7"
GORELEASER_VER: "v1.14.1" GORELEASER_VER: "v1.14.1"
concurrency: concurrency:

View File

@@ -12,11 +12,7 @@ builds:
- arm - arm
- amd64 - amd64
- arm64 - arm64
- mips
- 386 - 386
gomips:
- hardfloat
- softfloat
ignore: ignore:
- goos: windows - goos: windows
goarch: arm64 goarch: arm64
@@ -30,6 +26,26 @@ builds:
tags: tags:
- load_wgnt_from_rsrc - load_wgnt_from_rsrc
- id: netbird-static
dir: client
binary: netbird
env: [CGO_ENABLED=0]
goos:
- linux
goarch:
- mips
- mipsle
- mips64
- mips64le
gomips:
- hardfloat
- softfloat
ldflags:
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
mod_timestamp: '{{ .CommitTimestamp }}'
tags:
- load_wgnt_from_rsrc
- id: netbird-mgmt - id: netbird-mgmt
dir: management dir: management
env: env:
@@ -67,6 +83,7 @@ builds:
archives: archives:
- builds: - builds:
- netbird - netbird
- netbird-static
nfpms: nfpms:
@@ -359,4 +376,4 @@ uploads:
mode: archive mode: archive
target: https://pkgs.wiretrustee.com/yum/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }} target: https://pkgs.wiretrustee.com/yum/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }}
username: dev@wiretrustee.com username: dev@wiretrustee.com
method: PUT method: PUT

59
base62/base62.go Normal file
View File

@@ -0,0 +1,59 @@
package base62
import (
"fmt"
"math"
"strings"
)
const (
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
base = uint32(len(alphabet))
)
// Encode encodes a uint32 value to a base62 string.
func Encode(num uint32) string {
if num == 0 {
return string(alphabet[0])
}
var encoded strings.Builder
remainder := uint32(0)
for num > 0 {
remainder = num % base
encoded.WriteByte(alphabet[remainder])
num /= base
}
// Reverse the encoded string
encodedString := encoded.String()
reversed := reverse(encodedString)
return reversed
}
// Decode decodes a base62 string to a uint32 value.
func Decode(encoded string) (uint32, error) {
var decoded uint32
strLen := len(encoded)
for i, char := range encoded {
index := strings.IndexRune(alphabet, char)
if index < 0 {
return 0, fmt.Errorf("invalid character: %c", char)
}
decoded += uint32(index) * uint32(math.Pow(float64(base), float64(strLen-i-1)))
}
return decoded, nil
}
// Reverse a string.
func reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}

31
base62/base62_test.go Normal file
View File

@@ -0,0 +1,31 @@
package base62
import (
"testing"
)
func TestEncodeDecode(t *testing.T) {
tests := []struct {
num uint32
}{
{0},
{1},
{42},
{12345},
{99999},
{123456789},
}
for _, tt := range tests {
encoded := Encode(tt.num)
decoded, err := Decode(encoded)
if err != nil {
t.Errorf("Decode error: %v", err)
}
if decoded != tt.num {
t.Errorf("Decode(%v) = %v, want %v", encoded, decoded, tt.num)
}
}
}

View File

@@ -2,21 +2,23 @@ package cmd
import ( import (
"context" "context"
"github.com/netbirdio/netbird/management/server/activity"
"net" "net"
"path/filepath" "path/filepath"
"testing" "testing"
"time" "time"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/util" "github.com/netbirdio/netbird/util"
"google.golang.org/grpc"
clientProto "github.com/netbirdio/netbird/client/proto" clientProto "github.com/netbirdio/netbird/client/proto"
client "github.com/netbirdio/netbird/client/server" client "github.com/netbirdio/netbird/client/server"
mgmtProto "github.com/netbirdio/netbird/management/proto" mgmtProto "github.com/netbirdio/netbird/management/proto"
mgmt "github.com/netbirdio/netbird/management/server" mgmt "github.com/netbirdio/netbird/management/server"
sigProto "github.com/netbirdio/netbird/signal/proto" sigProto "github.com/netbirdio/netbird/signal/proto"
sig "github.com/netbirdio/netbird/signal/server" sig "github.com/netbirdio/netbird/signal/server"
"google.golang.org/grpc"
) )
func startTestingServices(t *testing.T) string { func startTestingServices(t *testing.T) string {
@@ -63,7 +65,7 @@ func startManagement(t *testing.T, config *mgmt.Config) (*grpc.Server, net.Liste
t.Fatal(err) t.Fatal(err)
} }
s := grpc.NewServer() s := grpc.NewServer()
store, err := mgmt.NewFileStore(config.Datadir) store, err := mgmt.NewFileStore(config.Datadir, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -32,6 +32,10 @@ func newFileConfigurator() (hostManager, error) {
return &fileConfigurator{}, nil return &fileConfigurator{}, nil
} }
func (f *fileConfigurator) supportCustomPort() bool {
return false
}
func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error { func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error {
backupFileExist := false backupFileExist := false
_, err := os.Stat(fileDefaultResolvConfBackupLocation) _, err := os.Stat(fileDefaultResolvConfBackupLocation)

View File

@@ -10,6 +10,7 @@ import (
type hostManager interface { type hostManager interface {
applyDNSConfig(config hostDNSConfig) error applyDNSConfig(config hostDNSConfig) error
restoreHostDNS() error restoreHostDNS() error
supportCustomPort() bool
} }
type hostDNSConfig struct { type hostDNSConfig struct {
@@ -26,8 +27,9 @@ type domainConfig struct {
} }
type mockHostConfigurator struct { type mockHostConfigurator struct {
applyDNSConfigFunc func(config hostDNSConfig) error applyDNSConfigFunc func(config hostDNSConfig) error
restoreHostDNSFunc func() error restoreHostDNSFunc func() error
supportCustomPortFunc func() bool
} }
func (m *mockHostConfigurator) applyDNSConfig(config hostDNSConfig) error { func (m *mockHostConfigurator) applyDNSConfig(config hostDNSConfig) error {
@@ -44,10 +46,18 @@ func (m *mockHostConfigurator) restoreHostDNS() error {
return fmt.Errorf("method restoreHostDNS is not implemented") return fmt.Errorf("method restoreHostDNS is not implemented")
} }
func (m *mockHostConfigurator) supportCustomPort() bool {
if m.supportCustomPortFunc != nil {
return m.supportCustomPortFunc()
}
return false
}
func newNoopHostMocker() hostManager { func newNoopHostMocker() hostManager {
return &mockHostConfigurator{ return &mockHostConfigurator{
applyDNSConfigFunc: func(config hostDNSConfig) error { return nil }, applyDNSConfigFunc: func(config hostDNSConfig) error { return nil },
restoreHostDNSFunc: func() error { return nil }, restoreHostDNSFunc: func() error { return nil },
supportCustomPortFunc: func() bool { return true },
} }
} }

View File

@@ -8,8 +8,9 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/netbirdio/netbird/iface"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/iface"
) )
const ( const (
@@ -39,6 +40,10 @@ func newHostManager(_ *iface.WGIface) (hostManager, error) {
}, nil }, nil
} }
func (s *systemConfigurator) supportCustomPort() bool {
return true
}
func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error { func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error {
var err error var err error

View File

@@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/netbirdio/netbird/iface"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/sys/windows/registry" "golang.org/x/sys/windows/registry"
"github.com/netbirdio/netbird/iface"
) )
const ( const (
@@ -42,6 +43,10 @@ func newHostManager(wgInterface *iface.WGIface) (hostManager, error) {
}, nil }, nil
} }
func (s *registryConfigurator) supportCustomPort() bool {
return false
}
func (r *registryConfigurator) applyDNSConfig(config hostDNSConfig) error { func (r *registryConfigurator) applyDNSConfig(config hostDNSConfig) error {
var err error var err error
if config.routeAll { if config.routeAll {

View File

@@ -2,10 +2,12 @@ package dns
import ( import (
"fmt" "fmt"
"github.com/miekg/dns"
nbdns "github.com/netbirdio/netbird/dns"
log "github.com/sirupsen/logrus"
"sync" "sync"
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
nbdns "github.com/netbirdio/netbird/dns"
) )
type registrationMap map[string]struct{} type registrationMap map[string]struct{}
@@ -15,6 +17,9 @@ type localResolver struct {
records sync.Map records sync.Map
} }
func (d *localResolver) stop() {
}
// ServeDNS handles a DNS request // ServeDNS handles a DNS request
func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
log.Tracef("received question: %#v\n", r.Question[0]) log.Tracef("received question: %#v\n", r.Question[0])

View File

@@ -11,8 +11,9 @@ import (
"github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5"
"github.com/hashicorp/go-version" "github.com/hashicorp/go-version"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/netbirdio/netbird/iface"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/iface"
) )
const ( const (
@@ -88,6 +89,10 @@ func newNetworkManagerDbusConfigurator(wgInterface *iface.WGIface) (hostManager,
}, nil }, nil
} }
func (n *networkManagerDbusConfigurator) supportCustomPort() bool {
return false
}
func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) error { func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) error {
connSettings, configVersion, err := n.getAppliedConnectionSettings() connSettings, configVersion, err := n.getAppliedConnectionSettings()
if err != nil { if err != nil {

View File

@@ -5,8 +5,9 @@ import (
"os/exec" "os/exec"
"strings" "strings"
"github.com/netbirdio/netbird/iface"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/iface"
) )
const resolvconfCommand = "resolvconf" const resolvconfCommand = "resolvconf"
@@ -21,6 +22,10 @@ func newResolvConfConfigurator(wgInterface *iface.WGIface) (hostManager, error)
}, nil }, nil
} }
func (r *resolvconf) supportCustomPort() bool {
return false
}
func (r *resolvconf) applyDNSConfig(config hostDNSConfig) error { func (r *resolvconf) applyDNSConfig(config hostDNSConfig) error {
var err error var err error
if !config.routeAll { if !config.routeAll {

View File

@@ -13,9 +13,10 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/mitchellh/hashstructure/v2" "github.com/mitchellh/hashstructure/v2"
log "github.com/sirupsen/logrus"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/iface" "github.com/netbirdio/netbird/iface"
log "github.com/sirupsen/logrus"
) )
const ( const (
@@ -25,15 +26,16 @@ const (
customIP = "127.0.0.153" customIP = "127.0.0.153"
) )
type registeredHandlerMap map[string]handlerWithStop
// DefaultServer dns server object // DefaultServer dns server object
type DefaultServer struct { type DefaultServer struct {
ctx context.Context ctx context.Context
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
upstreamCtxCancel context.CancelFunc
mux sync.Mutex mux sync.Mutex
server *dns.Server server *dns.Server
dnsMux *dns.ServeMux dnsMux *dns.ServeMux
dnsMuxMap registrationMap dnsMuxMap registeredHandlerMap
localResolver *localResolver localResolver *localResolver
wgInterface *iface.WGIface wgInterface *iface.WGIface
hostManager hostManager hostManager hostManager
@@ -46,9 +48,14 @@ type DefaultServer struct {
customAddress *netip.AddrPort customAddress *netip.AddrPort
} }
type handlerWithStop interface {
dns.Handler
stop()
}
type muxUpdate struct { type muxUpdate struct {
domain string domain string
handler dns.Handler handler handlerWithStop
} }
// NewDefaultServer returns a new dns server // NewDefaultServer returns a new dns server
@@ -78,7 +85,7 @@ func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface, customAdd
ctxCancel: stop, ctxCancel: stop,
server: dnsServer, server: dnsServer,
dnsMux: mux, dnsMux: mux,
dnsMuxMap: make(registrationMap), dnsMuxMap: make(registeredHandlerMap),
localResolver: &localResolver{ localResolver: &localResolver{
registeredMap: make(registrationMap), registeredMap: make(registrationMap),
}, },
@@ -254,7 +261,14 @@ func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
s.updateLocalResolver(localRecords) s.updateLocalResolver(localRecords)
s.currentConfig = dnsConfigToHostDNSConfig(update, s.runtimeIP, s.runtimePort) s.currentConfig = dnsConfigToHostDNSConfig(update, s.runtimeIP, s.runtimePort)
if err = s.hostManager.applyDNSConfig(s.currentConfig); err != nil { hostUpdate := s.currentConfig
if s.runtimePort != defaultPort && !s.hostManager.supportCustomPort() {
log.Warnf("the DNS manager of this peer doesn't support custom port. Disabling primary DNS setup. " +
"Learn more at: https://netbird.io/docs/how-to-guides/nameservers#local-resolver")
hostUpdate.routeAll = false
}
if err = s.hostManager.applyDNSConfig(hostUpdate); err != nil {
log.Error(err) log.Error(err)
} }
@@ -289,10 +303,6 @@ func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone)
} }
func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.NameServerGroup) ([]muxUpdate, error) { func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.NameServerGroup) ([]muxUpdate, error) {
// clean up the previous upstream resolver
if s.upstreamCtxCancel != nil {
s.upstreamCtxCancel()
}
var muxUpdates []muxUpdate var muxUpdates []muxUpdate
for _, nsGroup := range nameServerGroups { for _, nsGroup := range nameServerGroups {
@@ -301,10 +311,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
continue continue
} }
var ctx context.Context handler := newUpstreamResolver(s.ctx)
ctx, s.upstreamCtxCancel = context.WithCancel(s.ctx)
handler := newUpstreamResolver(ctx)
for _, ns := range nsGroup.NameServers { for _, ns := range nsGroup.NameServers {
if ns.NSType != nbdns.UDPNameServerType { if ns.NSType != nbdns.UDPNameServerType {
log.Warnf("skiping nameserver %s with type %s, this peer supports only %s", log.Warnf("skiping nameserver %s with type %s, this peer supports only %s",
@@ -315,6 +322,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
} }
if len(handler.upstreamServers) == 0 { if len(handler.upstreamServers) == 0 {
handler.stop()
log.Errorf("received a nameserver group with an invalid nameserver list") log.Errorf("received a nameserver group with an invalid nameserver list")
continue continue
} }
@@ -338,11 +346,13 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
} }
if len(nsGroup.Domains) == 0 { if len(nsGroup.Domains) == 0 {
handler.stop()
return nil, fmt.Errorf("received a non primary nameserver group with an empty domain list") return nil, fmt.Errorf("received a non primary nameserver group with an empty domain list")
} }
for _, domain := range nsGroup.Domains { for _, domain := range nsGroup.Domains {
if domain == "" { if domain == "" {
handler.stop()
return nil, fmt.Errorf("received a nameserver group with an empty domain element") return nil, fmt.Errorf("received a nameserver group with an empty domain element")
} }
muxUpdates = append(muxUpdates, muxUpdate{ muxUpdates = append(muxUpdates, muxUpdate{
@@ -355,16 +365,20 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
} }
func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) { func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) {
muxUpdateMap := make(registrationMap) muxUpdateMap := make(registeredHandlerMap)
for _, update := range muxUpdates { for _, update := range muxUpdates {
s.registerMux(update.domain, update.handler) s.registerMux(update.domain, update.handler)
muxUpdateMap[update.domain] = struct{}{} muxUpdateMap[update.domain] = update.handler
if existingHandler, ok := s.dnsMuxMap[update.domain]; ok {
existingHandler.stop()
}
} }
for key := range s.dnsMuxMap { for key, existingHandler := range s.dnsMuxMap {
_, found := muxUpdateMap[key] _, found := muxUpdateMap[key]
if !found { if !found {
existingHandler.stop()
s.deregisterMux(key) s.deregisterMux(key)
} }
} }

View File

@@ -41,21 +41,23 @@ func TestUpdateDNSServer(t *testing.T) {
}, },
} }
dummyHandler := &localResolver{}
testCases := []struct { testCases := []struct {
name string name string
initUpstreamMap registrationMap initUpstreamMap registeredHandlerMap
initLocalMap registrationMap initLocalMap registrationMap
initSerial uint64 initSerial uint64
inputSerial uint64 inputSerial uint64
inputUpdate nbdns.Config inputUpdate nbdns.Config
shouldFail bool shouldFail bool
expectedUpstreamMap registrationMap expectedUpstreamMap registeredHandlerMap
expectedLocalMap registrationMap expectedLocalMap registrationMap
}{ }{
{ {
name: "Initial Config Should Succeed", name: "Initial Config Should Succeed",
initLocalMap: make(registrationMap), initLocalMap: make(registrationMap),
initUpstreamMap: make(registrationMap), initUpstreamMap: make(registeredHandlerMap),
initSerial: 0, initSerial: 0,
inputSerial: 1, inputSerial: 1,
inputUpdate: nbdns.Config{ inputUpdate: nbdns.Config{
@@ -77,13 +79,13 @@ func TestUpdateDNSServer(t *testing.T) {
}, },
}, },
}, },
expectedUpstreamMap: registrationMap{"netbird.io": struct{}{}, "netbird.cloud": struct{}{}, nbdns.RootZone: struct{}{}}, expectedUpstreamMap: registeredHandlerMap{"netbird.io": dummyHandler, "netbird.cloud": dummyHandler, nbdns.RootZone: dummyHandler},
expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}}, expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}},
}, },
{ {
name: "New Config Should Succeed", name: "New Config Should Succeed",
initLocalMap: registrationMap{"netbird.cloud": struct{}{}}, initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
initUpstreamMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}}, initUpstreamMap: registeredHandlerMap{buildRecordKey(zoneRecords[0].Name, 1, 1): dummyHandler},
initSerial: 0, initSerial: 0,
inputSerial: 1, inputSerial: 1,
inputUpdate: nbdns.Config{ inputUpdate: nbdns.Config{
@@ -101,13 +103,13 @@ func TestUpdateDNSServer(t *testing.T) {
}, },
}, },
}, },
expectedUpstreamMap: registrationMap{"netbird.io": struct{}{}, "netbird.cloud": struct{}{}}, expectedUpstreamMap: registeredHandlerMap{"netbird.io": dummyHandler, "netbird.cloud": dummyHandler},
expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}}, expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}},
}, },
{ {
name: "Smaller Config Serial Should Be Skipped", name: "Smaller Config Serial Should Be Skipped",
initLocalMap: make(registrationMap), initLocalMap: make(registrationMap),
initUpstreamMap: make(registrationMap), initUpstreamMap: make(registeredHandlerMap),
initSerial: 2, initSerial: 2,
inputSerial: 1, inputSerial: 1,
shouldFail: true, shouldFail: true,
@@ -115,7 +117,7 @@ func TestUpdateDNSServer(t *testing.T) {
{ {
name: "Empty NS Group Domain Or Not Primary Element Should Fail", name: "Empty NS Group Domain Or Not Primary Element Should Fail",
initLocalMap: make(registrationMap), initLocalMap: make(registrationMap),
initUpstreamMap: make(registrationMap), initUpstreamMap: make(registeredHandlerMap),
initSerial: 0, initSerial: 0,
inputSerial: 1, inputSerial: 1,
inputUpdate: nbdns.Config{ inputUpdate: nbdns.Config{
@@ -137,7 +139,7 @@ func TestUpdateDNSServer(t *testing.T) {
{ {
name: "Invalid NS Group Nameservers list Should Fail", name: "Invalid NS Group Nameservers list Should Fail",
initLocalMap: make(registrationMap), initLocalMap: make(registrationMap),
initUpstreamMap: make(registrationMap), initUpstreamMap: make(registeredHandlerMap),
initSerial: 0, initSerial: 0,
inputSerial: 1, inputSerial: 1,
inputUpdate: nbdns.Config{ inputUpdate: nbdns.Config{
@@ -159,7 +161,7 @@ func TestUpdateDNSServer(t *testing.T) {
{ {
name: "Invalid Custom Zone Records list Should Fail", name: "Invalid Custom Zone Records list Should Fail",
initLocalMap: make(registrationMap), initLocalMap: make(registrationMap),
initUpstreamMap: make(registrationMap), initUpstreamMap: make(registeredHandlerMap),
initSerial: 0, initSerial: 0,
inputSerial: 1, inputSerial: 1,
inputUpdate: nbdns.Config{ inputUpdate: nbdns.Config{
@@ -181,21 +183,21 @@ func TestUpdateDNSServer(t *testing.T) {
{ {
name: "Empty Config Should Succeed and Clean Maps", name: "Empty Config Should Succeed and Clean Maps",
initLocalMap: registrationMap{"netbird.cloud": struct{}{}}, initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
initUpstreamMap: registrationMap{zoneRecords[0].Name: struct{}{}}, initUpstreamMap: registeredHandlerMap{zoneRecords[0].Name: dummyHandler},
initSerial: 0, initSerial: 0,
inputSerial: 1, inputSerial: 1,
inputUpdate: nbdns.Config{ServiceEnable: true}, inputUpdate: nbdns.Config{ServiceEnable: true},
expectedUpstreamMap: make(registrationMap), expectedUpstreamMap: make(registeredHandlerMap),
expectedLocalMap: make(registrationMap), expectedLocalMap: make(registrationMap),
}, },
{ {
name: "Disabled Service Should clean map", name: "Disabled Service Should clean map",
initLocalMap: registrationMap{"netbird.cloud": struct{}{}}, initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
initUpstreamMap: registrationMap{zoneRecords[0].Name: struct{}{}}, initUpstreamMap: registeredHandlerMap{zoneRecords[0].Name: dummyHandler},
initSerial: 0, initSerial: 0,
inputSerial: 1, inputSerial: 1,
inputUpdate: nbdns.Config{ServiceEnable: false}, inputUpdate: nbdns.Config{ServiceEnable: false},
expectedUpstreamMap: make(registrationMap), expectedUpstreamMap: make(registeredHandlerMap),
expectedLocalMap: make(registrationMap), expectedLocalMap: make(registrationMap),
}, },
} }
@@ -431,7 +433,7 @@ func getDefaultServerWithNoHostManager(t *testing.T, addrPort string) *DefaultSe
ctxCancel: cancel, ctxCancel: cancel,
server: dnsServer, server: dnsServer,
dnsMux: mux, dnsMux: mux,
dnsMuxMap: make(registrationMap), dnsMuxMap: make(registeredHandlerMap),
localResolver: &localResolver{ localResolver: &localResolver{
registeredMap: make(registrationMap), registeredMap: make(registrationMap),
}, },

View File

@@ -9,10 +9,11 @@ import (
"github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5"
"github.com/miekg/dns" "github.com/miekg/dns"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/iface"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/iface"
) )
const ( const (
@@ -75,6 +76,10 @@ func newSystemdDbusConfigurator(wgInterface *iface.WGIface) (hostManager, error)
}, nil }, nil
} }
func (s *systemdDbusConfigurator) supportCustomPort() bool {
return true
}
func (s *systemdDbusConfigurator) applyDNSConfig(config hostDNSConfig) error { func (s *systemdDbusConfigurator) applyDNSConfig(config hostDNSConfig) error {
parsedIP, err := netip.ParseAddr(config.serverIP) parsedIP, err := netip.ParseAddr(config.serverIP)
if err != nil { if err != nil {

View File

@@ -3,24 +3,31 @@ package dns
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"net" "net"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/cenkalti/backoff/v4"
"github.com/miekg/dns" "github.com/miekg/dns"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
const ( const (
failsTillDeact = int32(3) failsTillDeact = int32(5)
reactivatePeriod = time.Minute reactivatePeriod = 30 * time.Second
upstreamTimeout = 15 * time.Second upstreamTimeout = 15 * time.Second
) )
type upstreamClient interface {
ExchangeContext(ctx context.Context, m *dns.Msg, a string) (r *dns.Msg, rtt time.Duration, err error)
}
type upstreamResolver struct { type upstreamResolver struct {
ctx context.Context ctx context.Context
upstreamClient *dns.Client cancel context.CancelFunc
upstreamClient upstreamClient
upstreamServers []string upstreamServers []string
disabled bool disabled bool
failsCount atomic.Int32 failsCount atomic.Int32
@@ -33,9 +40,11 @@ type upstreamResolver struct {
reactivate func() reactivate func()
} }
func newUpstreamResolver(ctx context.Context) *upstreamResolver { func newUpstreamResolver(parentCTX context.Context) *upstreamResolver {
ctx, cancel := context.WithCancel(parentCTX)
return &upstreamResolver{ return &upstreamResolver{
ctx: ctx, ctx: ctx,
cancel: cancel,
upstreamClient: &dns.Client{}, upstreamClient: &dns.Client{},
upstreamTimeout: upstreamTimeout, upstreamTimeout: upstreamTimeout,
reactivatePeriod: reactivatePeriod, reactivatePeriod: reactivatePeriod,
@@ -43,6 +52,11 @@ func newUpstreamResolver(ctx context.Context) *upstreamResolver {
} }
} }
func (u *upstreamResolver) stop() {
log.Debugf("stoping serving DNS for upstreams %s", u.upstreamServers)
u.cancel()
}
// ServeDNS handles a DNS request // ServeDNS handles a DNS request
func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
defer u.checkUpstreamFails() defer u.checkUpstreamFails()
@@ -107,28 +121,57 @@ func (u *upstreamResolver) checkUpstreamFails() {
log.Warnf("upstream resolving is disabled for %v", reactivatePeriod) log.Warnf("upstream resolving is disabled for %v", reactivatePeriod)
u.deactivate() u.deactivate()
u.disabled = true u.disabled = true
go u.waitUntilReactivation() go u.waitUntilResponse()
} }
} }
// waitUntilReactivation reset fails counter and activates upstream resolving // waitUntilResponse retries, in an exponential interval, querying the upstream servers until it gets a positive response
func (u *upstreamResolver) waitUntilReactivation() { func (u *upstreamResolver) waitUntilResponse() {
timer := time.NewTimer(u.reactivatePeriod) exponentialBackOff := &backoff.ExponentialBackOff{
defer func() { InitialInterval: 500 * time.Millisecond,
if !timer.Stop() { RandomizationFactor: 0.5,
<-timer.C Multiplier: 1.1,
} MaxInterval: u.reactivatePeriod,
}() MaxElapsedTime: 0,
Stop: backoff.Stop,
select { Clock: backoff.SystemClock,
case <-u.ctx.Done():
return
case <-timer.C:
log.Info("upstream resolving is reactivated")
u.failsCount.Store(0)
u.reactivate()
u.disabled = false
} }
r := new(dns.Msg).SetQuestion("netbird.io.", dns.TypeA)
operation := func() error {
select {
case <-u.ctx.Done():
return backoff.Permanent(fmt.Errorf("exiting upstream retry loop for upstreams %s: parent context has been canceled", u.upstreamServers))
default:
}
var err error
for _, upstream := range u.upstreamServers {
ctx, cancel := context.WithTimeout(u.ctx, u.upstreamTimeout)
_, _, err = u.upstreamClient.ExchangeContext(ctx, r, upstream)
cancel()
if err == nil {
return nil
}
}
log.Tracef("checking connectivity with upstreams %s failed with error: %s. Retrying in %s", err, u.upstreamServers, exponentialBackOff.NextBackOff())
return fmt.Errorf("got an error from upstream check call")
}
err := backoff.Retry(operation, exponentialBackOff)
if err != nil {
log.Warn(err)
return
}
log.Infof("upstreams %s are responsive again. Adding them back to system", u.upstreamServers)
u.failsCount.Store(0)
u.reactivate()
u.disabled = false
} }
// isTimeout returns true if the given error is a network timeout error. // isTimeout returns true if the given error is a network timeout error.

View File

@@ -2,10 +2,11 @@ package dns
import ( import (
"context" "context"
"github.com/miekg/dns"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/miekg/dns"
) )
func TestUpstreamResolver_ServeDNS(t *testing.T) { func TestUpstreamResolver_ServeDNS(t *testing.T) {
@@ -106,8 +107,29 @@ func TestUpstreamResolver_ServeDNS(t *testing.T) {
} }
} }
type mockUpstreamResolver struct {
r *dns.Msg
rtt time.Duration
err error
}
// ExchangeContext mock implementation of ExchangeContext from upstreamResolver
func (c mockUpstreamResolver) ExchangeContext(_ context.Context, _ *dns.Msg, _ string) (r *dns.Msg, rtt time.Duration, err error) {
return c.r, c.rtt, c.err
}
func TestUpstreamResolver_DeactivationReactivation(t *testing.T) { func TestUpstreamResolver_DeactivationReactivation(t *testing.T) {
resolver := newUpstreamResolver(context.TODO()) resolver := &upstreamResolver{
ctx: context.TODO(),
upstreamClient: &mockUpstreamResolver{
err: nil,
r: new(dns.Msg),
rtt: time.Millisecond,
},
upstreamTimeout: upstreamTimeout,
reactivatePeriod: reactivatePeriod,
failsTillDeact: failsTillDeact,
}
resolver.upstreamServers = []string{"0.0.0.0:-1"} resolver.upstreamServers = []string{"0.0.0.0:-1"}
resolver.failsTillDeact = 0 resolver.failsTillDeact = 0
resolver.reactivatePeriod = time.Microsecond * 100 resolver.reactivatePeriod = time.Microsecond * 100

View File

@@ -3,8 +3,6 @@ package internal
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/netbirdio/netbird/iface/bind"
"github.com/pion/transport/v2/stdnet"
"net" "net"
"net/netip" "net/netip"
"os" "os"
@@ -15,6 +13,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/pion/transport/v2/stdnet"
"github.com/netbirdio/netbird/iface/bind"
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/stretchr/testify/require"
@@ -1039,7 +1041,7 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
return nil, "", err return nil, "", err
} }
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
store, err := server.NewFileStore(config.Datadir) store, err := server.NewFileStore(config.Datadir, nil)
if err != nil { if err != nil {
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err) log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
} }

View File

@@ -62,6 +62,16 @@ func removeFromRouteTable(prefix netip.Prefix) error {
} }
func enableIPForwarding() error { func enableIPForwarding() error {
err := os.WriteFile(ipv4ForwardingPath, []byte("1"), 0644) bytes, err := os.ReadFile(ipv4ForwardingPath)
return err if err != nil {
return err
}
// check if it is already enabled
// see more: https://github.com/netbirdio/netbird/issues/872
if len(bytes) > 0 && bytes[0] == 49 {
return nil
}
return os.WriteFile(ipv4ForwardingPath, []byte("1"), 0644)
} }

View File

@@ -1,8 +1,9 @@
package main package main
import ( import (
"github.com/netbirdio/netbird/client/cmd"
"os" "os"
"github.com/netbirdio/netbird/client/cmd"
) )
func main() { func main() {

View File

@@ -236,7 +236,9 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
}, nil }, nil
} else { } else {
log.Warnf("canceling previous waiting execution") log.Warnf("canceling previous waiting execution")
s.oauthAuthFlow.waitCancel() if s.oauthAuthFlow.waitCancel != nil {
s.oauthAuthFlow.waitCancel()
}
} }
} }

7
go.mod
View File

@@ -21,14 +21,13 @@ require (
golang.org/x/sys v0.6.0 golang.org/x/sys v0.6.0
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de
golang.zx2c4.com/wireguard/windows v0.5.1 golang.zx2c4.com/wireguard/windows v0.5.3
google.golang.org/grpc v1.52.3 google.golang.org/grpc v1.52.3
google.golang.org/protobuf v1.28.1 google.golang.org/protobuf v1.28.1
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0
) )
require ( require (
codeberg.org/ac/base62 v0.0.0-20210305150220-e793b546833a
fyne.io/fyne/v2 v2.1.4 fyne.io/fyne/v2 v2.1.4
github.com/c-robinson/iplib v1.0.3 github.com/c-robinson/iplib v1.0.3
github.com/coreos/go-iptables v0.6.0 github.com/coreos/go-iptables v0.6.0
@@ -57,6 +56,7 @@ require (
github.com/rs/xid v1.3.0 github.com/rs/xid v1.3.0
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/stretchr/testify v1.8.1 github.com/stretchr/testify v1.8.1
go.opentelemetry.io/otel v1.11.1
go.opentelemetry.io/otel/exporters/prometheus v0.33.0 go.opentelemetry.io/otel/exporters/prometheus v0.33.0
go.opentelemetry.io/otel/metric v0.33.0 go.opentelemetry.io/otel/metric v0.33.0
go.opentelemetry.io/otel/sdk/metric v0.33.0 go.opentelemetry.io/otel/sdk/metric v0.33.0
@@ -124,7 +124,6 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.1.0 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect
github.com/yuin/goldmark v1.4.13 // indirect github.com/yuin/goldmark v1.4.13 // indirect
go.opentelemetry.io/otel v1.11.1 // indirect
go.opentelemetry.io/otel/sdk v1.11.1 // indirect go.opentelemetry.io/otel/sdk v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect go.opentelemetry.io/otel/trace v1.11.1 // indirect
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect
@@ -144,4 +143,4 @@ replace github.com/kardianos/service => github.com/netbirdio/service v0.0.0-2023
replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20230426151838-5c7986a94d53 replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f

10
go.sum
View File

@@ -31,8 +31,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
codeberg.org/ac/base62 v0.0.0-20210305150220-e793b546833a h1:U6cY/g6VSiy59vuvnBU6J/eSir0qVg4BeTnCDLaX+20=
codeberg.org/ac/base62 v0.0.0-20210305150220-e793b546833a/go.mod h1:ykEpkLT4JtH3I4Rb4gwkDsNLfgUg803qRDeIX88t3e8=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
fyne.io/fyne/v2 v2.1.4 h1:bt1+28++kAzRzPB0GM2EuSV4cnl8rXNX4cjfd8G06Rc= fyne.io/fyne/v2 v2.1.4 h1:bt1+28++kAzRzPB0GM2EuSV4cnl8rXNX4cjfd8G06Rc=
fyne.io/fyne/v2 v2.1.4/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ= fyne.io/fyne/v2 v2.1.4/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ=
@@ -487,8 +485,8 @@ github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0 h1:hirFRfx3grVA/
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c h1:wK/s4nyZj/GF/kFJQjX6nqNfE0G3gcqd6hhnPCyp4sw= github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c h1:wK/s4nyZj/GF/kFJQjX6nqNfE0G3gcqd6hhnPCyp4sw=
github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM= github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM=
github.com/netbirdio/wireguard-go v0.0.0-20230426151838-5c7986a94d53 h1:OPbKpisDyMbOf/TDYS0Niw7yc/uoviED/pKyO+8A1C0= github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f h1:WQXGYCKPkNs1KusFTLieV73UVTNfZVyez4CFRvlOruM=
github.com/netbirdio/wireguard-go v0.0.0-20230426151838-5c7986a94d53/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4= github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -1039,8 +1037,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de h1:qDZ+lyO5jC9RNJ7ANJA0GWXk3pSn0Fu5SlcAIlgw+6w= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de h1:qDZ+lyO5jC9RNJ7ANJA0GWXk3pSn0Fu5SlcAIlgw+6w=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de/go.mod h1:Q2XNgour4QSkFj0BWCkVlW0HWJwQgNMsMahpSlI0Eno= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de/go.mod h1:Q2XNgour4QSkFj0BWCkVlW0HWJwQgNMsMahpSlI0Eno=
golang.zx2c4.com/wireguard/windows v0.5.1 h1:OnYw96PF+CsIMrqWo5QP3Q59q5hY1rFErk/yN3cS+JQ= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.1/go.mod h1:EApyTk/ZNrkbZjurHL1nleDYnsPpJYBO7LZEBCyDAHk= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=

View File

@@ -209,7 +209,7 @@ func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline
// otherwise, make a STUN request to discover the address // otherwise, make a STUN request to discover the address
// or wait for already sent request to complete // or wait for already sent request to complete
waitAddrReceived, err := m.sendStun(serverAddr) waitAddrReceived, err := m.sendSTUN(serverAddr)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s: %s", "failed to send STUN packet", err) return nil, fmt.Errorf("%s: %s", "failed to send STUN packet", err)
} }
@@ -218,23 +218,31 @@ func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline
select { select {
case <-waitAddrReceived: case <-waitAddrReceived:
// when channel closed, addr was obtained // when channel closed, addr was obtained
var addr *stun.XORMappedAddress
m.mu.Lock() m.mu.Lock()
mappedAddr := *m.xorMappedMap[serverAddr.String()] // A very odd case that mappedAddr is nil.
// Can happen when the deadline property is larger than params.XORMappedAddrCacheTTL.
// Or when we don't receive a response to our m.sendSTUN request (the response is handled asynchronously) and
// the XORMapped expires meanwhile triggering a closure of the waitAddrReceived channel.
// We protect the code from panic here.
if mappedAddr, ok := m.xorMappedMap[serverAddr.String()]; ok {
addr = mappedAddr.addr
}
m.mu.Unlock() m.mu.Unlock()
if mappedAddr.addr == nil { if addr == nil {
return nil, fmt.Errorf("no XOR address mapping") return nil, fmt.Errorf("no XOR address mapping")
} }
return mappedAddr.addr, nil return addr, nil
case <-time.After(deadline): case <-time.After(deadline):
return nil, fmt.Errorf("timeout while waiting for XORMappedAddr") return nil, fmt.Errorf("timeout while waiting for XORMappedAddr")
} }
} }
// sendStun sends a STUN request via UDP conn. // sendSTUN sends a STUN request via UDP conn.
// //
// The returned channel is closed when the STUN response has been received. // The returned channel is closed when the STUN response has been received.
// Method is safe for concurrent use. // Method is safe for concurrent use.
func (m *UniversalUDPMuxDefault) sendStun(serverAddr net.Addr) (chan struct{}, error) { func (m *UniversalUDPMuxDefault) sendSTUN(serverAddr net.Addr) (chan struct{}, error) {
m.mu.Lock() m.mu.Lock()
defer m.mu.Unlock() defer m.mu.Unlock()

View File

@@ -3,9 +3,11 @@ package iface
import ( import (
"fmt" "fmt"
"net" "net"
"net/netip"
"github.com/pion/transport/v2" "github.com/pion/transport/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/ipc" "golang.zx2c4.com/wireguard/ipc"
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
@@ -50,7 +52,6 @@ func (c *tunDevice) createWithUserspace() (NetInterface, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// We need to create a wireguard-go device and listen to configuration requests // We need to create a wireguard-go device and listen to configuration requests
tunDev := device.NewDevice(tunIface, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] ")) tunDev := device.NewDevice(tunIface, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] "))
err = tunDev.Up() err = tunDev.Up()
@@ -59,6 +60,22 @@ func (c *tunDevice) createWithUserspace() (NetInterface, error) {
return nil, err return nil, err
} }
luid := winipcfg.LUID(tunIface.(*tun.NativeTun).LUID())
nbiface, err := luid.IPInterface(windows.AF_INET)
if err != nil {
_ = tunIface.Close()
return nil, fmt.Errorf("got error when getting ip interface %s", err)
}
nbiface.NLMTU = uint32(c.mtu)
err = nbiface.Set()
if err != nil {
_ = tunIface.Close()
return nil, fmt.Errorf("got error when getting setting the interface mtu: %s", err)
}
c.uapi, err = c.getUAPI(c.name) c.uapi, err = c.getUAPI(c.name)
if err != nil { if err != nil {
_ = tunIface.Close() _ = tunIface.Close()
@@ -142,7 +159,7 @@ func (c *tunDevice) assignAddr() error {
tunDev := c.netInterface.(*tun.NativeTun) tunDev := c.netInterface.(*tun.NativeTun)
luid := winipcfg.LUID(tunDev.LUID()) luid := winipcfg.LUID(tunDev.LUID())
log.Debugf("adding address %s to interface: %s", c.address.IP, c.name) log.Debugf("adding address %s to interface: %s", c.address.IP, c.name)
return luid.SetIPAddresses([]net.IPNet{{c.address.IP, c.address.Network.Mask}}) return luid.SetIPAddresses([]netip.Prefix{netip.MustParsePrefix(c.address.String())})
} }
// getUAPI returns a Listener // getUAPI returns a Listener

View File

@@ -2,28 +2,31 @@ package client
import ( import (
"context" "context"
"github.com/netbirdio/netbird/management/server/activity"
"net" "net"
"path/filepath" "path/filepath"
"sync" "sync"
"testing" "testing"
"time" "time"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/client/system" "github.com/netbirdio/netbird/client/system"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/netbirdio/netbird/encryption" "github.com/netbirdio/netbird/encryption"
"github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/proto"
mgmtProto "github.com/netbirdio/netbird/management/proto" mgmtProto "github.com/netbirdio/netbird/management/proto"
mgmt "github.com/netbirdio/netbird/management/server" mgmt "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/mock_server" "github.com/netbirdio/netbird/management/server/mock_server"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/netbirdio/netbird/util"
"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/netbird/util"
) )
const ValidKey = "A2C8E62B-38F5-4553-B31E-DD66C696CEBB" const ValidKey = "A2C8E62B-38F5-4553-B31E-DD66C696CEBB"
@@ -50,7 +53,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
t.Fatal(err) t.Fatal(err)
} }
s := grpc.NewServer() s := grpc.NewServer()
store, err := mgmt.NewFileStore(config.Datadir) store, err := mgmt.NewFileStore(config.Datadir, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -121,13 +121,6 @@ var (
return fmt.Errorf("failed creating datadir: %s: %v", config.Datadir, err) return fmt.Errorf("failed creating datadir: %s: %v", config.Datadir, err)
} }
} }
store, err := server.NewFileStore(config.Datadir)
if err != nil {
return fmt.Errorf("failed creating Store: %s: %v", config.Datadir, err)
}
peersUpdateManager := server.NewPeersUpdateManager()
appMetrics, err := telemetry.NewDefaultAppMetrics(cmd.Context()) appMetrics, err := telemetry.NewDefaultAppMetrics(cmd.Context())
if err != nil { if err != nil {
return err return err
@@ -136,6 +129,11 @@ var (
if err != nil { if err != nil {
return err return err
} }
store, err := server.NewFileStore(config.Datadir, appMetrics)
if err != nil {
return fmt.Errorf("failed creating Store: %s: %v", config.Datadir, err)
}
peersUpdateManager := server.NewPeersUpdateManager()
var idpManager idp.Manager var idpManager idp.Manager
if config.IdpManagerConfig != nil { if config.IdpManagerConfig != nil {

View File

@@ -15,13 +15,13 @@ import (
"sync" "sync"
"time" "time"
"codeberg.org/ac/base62"
"github.com/eko/gocache/v3/cache" "github.com/eko/gocache/v3/cache"
cacheStore "github.com/eko/gocache/v3/store" cacheStore "github.com/eko/gocache/v3/store"
gocache "github.com/patrickmn/go-cache" gocache "github.com/patrickmn/go-cache"
"github.com/rs/xid" "github.com/rs/xid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/base62"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/idp"

View File

@@ -1869,7 +1869,7 @@ func createManager(t *testing.T) (*DefaultAccountManager, error) {
func createStore(t *testing.T) (Store, error) { func createStore(t *testing.T) (Store, error) {
dataDir := t.TempDir() dataDir := t.TempDir()
store, err := NewFileStore(dataDir) store, err := NewFileStore(dataDir, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -1,10 +1,12 @@
package server package server
import ( import (
"testing"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/status" "github.com/netbirdio/netbird/management/server/status"
"github.com/stretchr/testify/require"
"testing"
) )
const ( const (
@@ -190,7 +192,7 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
func createDNSStore(t *testing.T) (Store, error) { func createDNSStore(t *testing.T) (Store, error) {
dataDir := t.TempDir() dataDir := t.TempDir()
store, err := NewFileStore(dataDir) store, err := NewFileStore(dataDir, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -11,6 +11,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server/status" "github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/util" "github.com/netbirdio/netbird/util"
) )
@@ -37,13 +38,20 @@ type FileStore struct {
// sync.Mutex indexed by accountID // sync.Mutex indexed by accountID
accountLocks sync.Map `json:"-"` accountLocks sync.Map `json:"-"`
globalAccountLock sync.Mutex `json:"-"` globalAccountLock sync.Mutex `json:"-"`
metrics telemetry.AppMetrics `json:"-"`
} }
type StoredAccount struct{} type StoredAccount struct{}
// NewFileStore restores a store from the file located in the datadir // NewFileStore restores a store from the file located in the datadir
func NewFileStore(dataDir string) (*FileStore, error) { func NewFileStore(dataDir string, metrics telemetry.AppMetrics) (*FileStore, error) {
return restore(filepath.Join(dataDir, storeFileName)) fs, err := restore(filepath.Join(dataDir, storeFileName))
if err != nil {
return nil, err
}
fs.metrics = metrics
return fs, nil
} }
// restore the state of the store from the file. // restore the state of the store from the file.
@@ -221,7 +229,17 @@ func restore(file string) (*FileStore, error) {
// persist account data to a file // persist account data to a file
// It is recommended to call it with locking FileStore.mux // It is recommended to call it with locking FileStore.mux
func (s *FileStore) persist(file string) error { func (s *FileStore) persist(file string) error {
return util.WriteJson(file, s) start := time.Now()
err := util.WriteJson(file, s)
if err != nil {
return err
}
took := time.Since(start)
if s.metrics != nil {
s.metrics.StoreMetrics().CountPersistenceDuration(took)
}
log.Debugf("took %d ms to persist the FileStore", took.Milliseconds())
return nil
} }
// AcquireGlobalLock acquires global lock across all the accounts and returns a function that releases the lock // AcquireGlobalLock acquires global lock across all the accounts and returns a function that releases the lock
@@ -235,6 +253,12 @@ func (s *FileStore) AcquireGlobalLock() (unlock func()) {
log.Debugf("released global lock in %v", time.Since(start)) log.Debugf("released global lock in %v", time.Since(start))
} }
took := time.Since(start)
log.Debugf("took %v to acquire global lock", took)
if s.metrics != nil {
s.metrics.StoreMetrics().CountGlobalLockAcquisitionDuration(took)
}
return unlock return unlock
} }

View File

@@ -25,7 +25,7 @@ func TestStalePeerIndices(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
return return
} }
@@ -172,7 +172,7 @@ func TestStore(t *testing.T) {
return return
} }
restored, err := NewFileStore(store.storeFile) restored, err := NewFileStore(store.storeFile, nil)
if err != nil { if err != nil {
return return
} }
@@ -232,7 +232,7 @@ func TestRestore(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
return return
} }
@@ -270,7 +270,7 @@ func TestRestorePolicies_Migration(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
return return
} }
@@ -307,7 +307,7 @@ func TestGetAccountByPrivateDomain(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
return return
} }
@@ -336,7 +336,7 @@ func TestFileStore_GetAccount(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -378,7 +378,7 @@ func TestFileStore_GetTokenIDByHashedToken(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -431,7 +431,7 @@ func TestFileStore_GetTokenIDByHashedToken_Failure(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -456,7 +456,7 @@ func TestFileStore_GetUserByTokenID(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -484,7 +484,7 @@ func TestFileStore_GetUserByTokenID_Failure(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -503,7 +503,7 @@ func TestFileStore_SavePeerStatus(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
store, err := NewFileStore(storeDir) store, err := NewFileStore(storeDir, nil)
if err != nil { if err != nil {
return return
} }
@@ -548,7 +548,7 @@ func TestFileStore_SavePeerStatus(t *testing.T) {
} }
func newStore(t *testing.T) *FileStore { func newStore(t *testing.T) *FileStore {
store, err := NewFileStore(t.TempDir()) store, err := NewFileStore(t.TempDir(), nil)
if err != nil { if err != nil {
t.Errorf("failed creating a new store") t.Errorf("failed creating a new store")
} }

View File

@@ -114,6 +114,7 @@ func (s *GRPCServer) GetServerKey(ctx context.Context, req *proto.Empty) (*proto
// Sync validates the existence of a connecting peer, sends an initial state (all available for the connecting peers) and // Sync validates the existence of a connecting peer, sends an initial state (all available for the connecting peers) and
// notifies the connected peer of any updates (e.g. new peers under the same account) // notifies the connected peer of any updates (e.g. new peers under the same account)
func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_SyncServer) error { func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_SyncServer) error {
reqStart := time.Now()
if s.appMetrics != nil { if s.appMetrics != nil {
s.appMetrics.GRPCMetrics().CountSyncRequest() s.appMetrics.GRPCMetrics().CountSyncRequest()
} }
@@ -148,6 +149,11 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
if s.config.TURNConfig.TimeBasedCredentials { if s.config.TURNConfig.TimeBasedCredentials {
s.turnCredentialsManager.SetupRefresh(peer.ID) s.turnCredentialsManager.SetupRefresh(peer.ID)
} }
if s.appMetrics != nil {
s.appMetrics.GRPCMetrics().CountSyncRequestDuration(time.Since(reqStart))
}
// keep a connection to the peer and send updates when available // keep a connection to the peer and send updates when available
for { for {
select { select {
@@ -262,6 +268,12 @@ func (s *GRPCServer) parseRequest(req *proto.EncryptedMessage, parsed pb.Message
// In case it isn't, the endpoint checks whether setup key is provided within the request and tries to register a peer. // In case it isn't, the endpoint checks whether setup key is provided within the request and tries to register a peer.
// In case of the successful registration login is also successful // In case of the successful registration login is also successful
func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) { func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) {
reqStart := time.Now()
defer func() {
if s.appMetrics != nil {
s.appMetrics.GRPCMetrics().CountLoginRequestDuration(time.Since(reqStart))
}
}()
if s.appMetrics != nil { if s.appMetrics != nil {
s.appMetrics.GRPCMetrics().CountLoginRequest() s.appMetrics.GRPCMetrics().CountLoginRequest()
} }

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,8 @@ const (
EventActivityCodePeerRename EventActivityCode = "peer.rename" EventActivityCodePeerRename EventActivityCode = "peer.rename"
EventActivityCodePeerSshDisable EventActivityCode = "peer.ssh.disable" EventActivityCodePeerSshDisable EventActivityCode = "peer.ssh.disable"
EventActivityCodePeerSshEnable EventActivityCode = "peer.ssh.enable" EventActivityCodePeerSshEnable EventActivityCode = "peer.ssh.enable"
EventActivityCodePersonalAccessTokenCreate EventActivityCode = "personal.access.token.create"
EventActivityCodePersonalAccessTokenDelete EventActivityCode = "personal.access.token.delete"
EventActivityCodePolicyAdd EventActivityCode = "policy.add" EventActivityCodePolicyAdd EventActivityCode = "policy.add"
EventActivityCodePolicyDelete EventActivityCode = "policy.delete" EventActivityCodePolicyDelete EventActivityCode = "policy.delete"
EventActivityCodePolicyUpdate EventActivityCode = "policy.update" EventActivityCodePolicyUpdate EventActivityCode = "policy.update"
@@ -39,6 +41,8 @@ const (
EventActivityCodeRuleAdd EventActivityCode = "rule.add" EventActivityCodeRuleAdd EventActivityCode = "rule.add"
EventActivityCodeRuleDelete EventActivityCode = "rule.delete" EventActivityCodeRuleDelete EventActivityCode = "rule.delete"
EventActivityCodeRuleUpdate EventActivityCode = "rule.update" EventActivityCodeRuleUpdate EventActivityCode = "rule.update"
EventActivityCodeServiceUserCreate EventActivityCode = "service.user.create"
EventActivityCodeServiceUserDelete EventActivityCode = "service.user.delete"
EventActivityCodeSetupkeyAdd EventActivityCode = "setupkey.add" EventActivityCodeSetupkeyAdd EventActivityCode = "setupkey.add"
EventActivityCodeSetupkeyGroupAdd EventActivityCode = "setupkey.group.add" EventActivityCodeSetupkeyGroupAdd EventActivityCode = "setupkey.group.add"
EventActivityCodeSetupkeyGroupDelete EventActivityCode = "setupkey.group.delete" EventActivityCodeSetupkeyGroupDelete EventActivityCode = "setupkey.group.delete"
@@ -151,6 +155,15 @@ type GroupMinimum struct {
PeersCount int `json:"peers_count"` PeersCount int `json:"peers_count"`
} }
// GroupRequest defines model for GroupRequest.
type GroupRequest struct {
// Name Group name identifier
Name string `json:"name"`
// Peers List of peers ids
Peers *[]string `json:"peers,omitempty"`
}
// Nameserver defines model for Nameserver. // Nameserver defines model for Nameserver.
type Nameserver struct { type Nameserver struct {
// Ip Nameserver IP // Ip Nameserver IP
@@ -277,6 +290,13 @@ type PeerMinimum struct {
Name string `json:"name"` Name string `json:"name"`
} }
// PeerRequest defines model for PeerRequest.
type PeerRequest struct {
LoginExpirationEnabled bool `json:"login_expiration_enabled"`
Name string `json:"name"`
SshEnabled bool `json:"ssh_enabled"`
}
// PersonalAccessToken defines model for PersonalAccessToken. // PersonalAccessToken defines model for PersonalAccessToken.
type PersonalAccessToken struct { type PersonalAccessToken struct {
// CreatedAt Date the token was created // CreatedAt Date the token was created
@@ -480,6 +500,27 @@ type RuleMinimum struct {
Name string `json:"name"` Name string `json:"name"`
} }
// RuleRequest defines model for RuleRequest.
type RuleRequest struct {
// Description Rule friendly description
Description string `json:"description"`
// Destinations List of destination groups
Destinations *[]string `json:"destinations,omitempty"`
// Disabled Rules status
Disabled bool `json:"disabled"`
// Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted
Flow string `json:"flow"`
// Name Rule name identifier
Name string `json:"name"`
// Sources List of source groups
Sources *[]string `json:"sources,omitempty"`
}
// SetupKey defines model for SetupKey. // SetupKey defines model for SetupKey.
type SetupKey struct { type SetupKey struct {
// AutoGroups Setup key groups to auto-assign to peers registered with this key // AutoGroups Setup key groups to auto-assign to peers registered with this key
@@ -611,65 +652,6 @@ type PutApiAccountsAccountIdJSONBody struct {
Settings AccountSettings `json:"settings"` Settings AccountSettings `json:"settings"`
} }
// PostApiGroupsJSONBody defines parameters for PostApiGroups.
type PostApiGroupsJSONBody struct {
Name string `json:"name"`
Peers *[]string `json:"peers,omitempty"`
}
// PutApiGroupsGroupIdJSONBody defines parameters for PutApiGroupsGroupId.
type PutApiGroupsGroupIdJSONBody struct {
Name *string `json:"Name,omitempty"`
Peers *[]string `json:"Peers,omitempty"`
}
// PutApiPeersPeerIdJSONBody defines parameters for PutApiPeersPeerId.
type PutApiPeersPeerIdJSONBody struct {
LoginExpirationEnabled bool `json:"login_expiration_enabled"`
Name string `json:"name"`
SshEnabled bool `json:"ssh_enabled"`
}
// PostApiPoliciesJSONBody defines parameters for PostApiPolicies.
type PostApiPoliciesJSONBody = PolicyMinimum
// PutApiPoliciesPolicyIdJSONBody defines parameters for PutApiPoliciesPolicyId.
type PutApiPoliciesPolicyIdJSONBody = PolicyMinimum
// PostApiRulesJSONBody defines parameters for PostApiRules.
type PostApiRulesJSONBody struct {
// Description Rule friendly description
Description string `json:"description"`
Destinations *[]string `json:"destinations,omitempty"`
// Disabled Rules status
Disabled bool `json:"disabled"`
// Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted
Flow string `json:"flow"`
// Name Rule name identifier
Name string `json:"name"`
Sources *[]string `json:"sources,omitempty"`
}
// PutApiRulesRuleIdJSONBody defines parameters for PutApiRulesRuleId.
type PutApiRulesRuleIdJSONBody struct {
// Description Rule friendly description
Description string `json:"description"`
Destinations *[]string `json:"destinations,omitempty"`
// Disabled Rules status
Disabled bool `json:"disabled"`
// Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted
Flow string `json:"flow"`
// Name Rule name identifier
Name string `json:"name"`
Sources *[]string `json:"sources,omitempty"`
}
// GetApiUsersParams defines parameters for GetApiUsers. // GetApiUsersParams defines parameters for GetApiUsers.
type GetApiUsersParams struct { type GetApiUsersParams struct {
// ServiceUser Filters users and returns either regular users or service users // ServiceUser Filters users and returns either regular users or service users
@@ -689,19 +671,19 @@ type PutApiDnsNameserversNsgroupIdJSONRequestBody = NameserverGroupRequest
type PutApiDnsSettingsJSONRequestBody = DNSSettings type PutApiDnsSettingsJSONRequestBody = DNSSettings
// PostApiGroupsJSONRequestBody defines body for PostApiGroups for application/json ContentType. // PostApiGroupsJSONRequestBody defines body for PostApiGroups for application/json ContentType.
type PostApiGroupsJSONRequestBody PostApiGroupsJSONBody type PostApiGroupsJSONRequestBody = GroupRequest
// PutApiGroupsGroupIdJSONRequestBody defines body for PutApiGroupsGroupId for application/json ContentType. // PutApiGroupsGroupIdJSONRequestBody defines body for PutApiGroupsGroupId for application/json ContentType.
type PutApiGroupsGroupIdJSONRequestBody PutApiGroupsGroupIdJSONBody type PutApiGroupsGroupIdJSONRequestBody = GroupRequest
// PutApiPeersPeerIdJSONRequestBody defines body for PutApiPeersPeerId for application/json ContentType. // PutApiPeersPeerIdJSONRequestBody defines body for PutApiPeersPeerId for application/json ContentType.
type PutApiPeersPeerIdJSONRequestBody PutApiPeersPeerIdJSONBody type PutApiPeersPeerIdJSONRequestBody = PeerRequest
// PostApiPoliciesJSONRequestBody defines body for PostApiPolicies for application/json ContentType. // PostApiPoliciesJSONRequestBody defines body for PostApiPolicies for application/json ContentType.
type PostApiPoliciesJSONRequestBody = PostApiPoliciesJSONBody type PostApiPoliciesJSONRequestBody = PolicyMinimum
// PutApiPoliciesPolicyIdJSONRequestBody defines body for PutApiPoliciesPolicyId for application/json ContentType. // PutApiPoliciesPolicyIdJSONRequestBody defines body for PutApiPoliciesPolicyId for application/json ContentType.
type PutApiPoliciesPolicyIdJSONRequestBody = PutApiPoliciesPolicyIdJSONBody type PutApiPoliciesPolicyIdJSONRequestBody = PolicyMinimum
// PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType. // PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType.
type PostApiRoutesJSONRequestBody = RouteRequest type PostApiRoutesJSONRequestBody = RouteRequest
@@ -710,10 +692,10 @@ type PostApiRoutesJSONRequestBody = RouteRequest
type PutApiRoutesRouteIdJSONRequestBody = RouteRequest type PutApiRoutesRouteIdJSONRequestBody = RouteRequest
// PostApiRulesJSONRequestBody defines body for PostApiRules for application/json ContentType. // PostApiRulesJSONRequestBody defines body for PostApiRules for application/json ContentType.
type PostApiRulesJSONRequestBody PostApiRulesJSONBody type PostApiRulesJSONRequestBody = RuleRequest
// PutApiRulesRuleIdJSONRequestBody defines body for PutApiRulesRuleId for application/json ContentType. // PutApiRulesRuleIdJSONRequestBody defines body for PutApiRulesRuleId for application/json ContentType.
type PutApiRulesRuleIdJSONRequestBody PutApiRulesRuleIdJSONBody type PutApiRulesRuleIdJSONRequestBody = RuleRequest
// PostApiSetupKeysJSONRequestBody defines body for PostApiSetupKeys for application/json ContentType. // PostApiSetupKeysJSONRequestBody defines body for PostApiSetupKeys for application/json ContentType.
type PostApiSetupKeysJSONRequestBody = SetupKeyRequest type PostApiSetupKeysJSONRequestBody = SetupKeyRequest

View File

@@ -95,7 +95,7 @@ func (h *GroupsHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
return return
} }
if *req.Name == "" { if req.Name == "" {
util.WriteError(status.Errorf(status.InvalidArgument, "group name shouldn't be empty"), w) util.WriteError(status.Errorf(status.InvalidArgument, "group name shouldn't be empty"), w)
return return
} }
@@ -108,7 +108,7 @@ func (h *GroupsHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
} }
group := server.Group{ group := server.Group{
ID: groupID, ID: groupID,
Name: *req.Name, Name: req.Name,
Peers: peers, Peers: peers,
} }

View File

@@ -42,7 +42,7 @@ func (h *PeersHandler) getPeer(account *server.Account, peerID, userID string, w
} }
func (h *PeersHandler) updatePeer(account *server.Account, user *server.User, peerID string, w http.ResponseWriter, r *http.Request) { func (h *PeersHandler) updatePeer(account *server.Account, user *server.User, peerID string, w http.ResponseWriter, r *http.Request) {
req := &api.PutApiPeersPeerIdJSONBody{} req := &api.PeerRequest{}
err := json.NewDecoder(r.Body).Decode(&req) err := json.NewDecoder(r.Body).Decode(&req)
if err != nil { if err != nil {
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w) util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)

View File

@@ -122,7 +122,7 @@ func NewAuth0Manager(oidcConfig OIDCConfig, config Auth0ClientConfig,
} }
helper := JsonParser{} helper := JsonParser{}
config.AuthIssuer = oidcConfig.TokenEndpoint config.AuthIssuer = oidcConfig.Issuer
config.GrantType = "client_credentials" config.GrantType = "client_credentials"
if config.ClientID == "" { if config.ClientID == "" {

View File

@@ -2,7 +2,6 @@ package server
import ( import (
"context" "context"
"github.com/netbirdio/netbird/management/server/activity"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
@@ -10,14 +9,17 @@ import (
"testing" "testing"
"time" "time"
"github.com/netbirdio/netbird/encryption" "github.com/netbirdio/netbird/management/server/activity"
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/util"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
"github.com/netbirdio/netbird/encryption"
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/util"
) )
var ( var (
@@ -408,7 +410,7 @@ func startManagement(t *testing.T, config *Config) (*grpc.Server, string, error)
return nil, "", err return nil, "", err
} }
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
store, err := NewFileStore(config.Datadir) store, err := NewFileStore(config.Datadir, nil)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }

View File

@@ -496,7 +496,7 @@ func startServer(config *server.Config) (*grpc.Server, net.Listener) {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
s := grpc.NewServer() s := grpc.NewServer()
store, err := server.NewFileStore(config.Datadir) store, err := server.NewFileStore(config.Datadir, nil)
if err != nil { if err != nil {
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err) log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
} }

View File

@@ -1,11 +1,13 @@
package server package server
import ( import (
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/stretchr/testify/require"
"net/netip" "net/netip"
"testing" "testing"
"github.com/stretchr/testify/require"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/activity"
) )
const ( const (
@@ -1064,7 +1066,7 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
func createNSStore(t *testing.T) (Store, error) { func createNSStore(t *testing.T) (Store, error) {
dataDir := t.TempDir() dataDir := t.TempDir()
store, err := NewFileStore(dataDir) store, err := NewFileStore(dataDir, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -28,6 +28,17 @@ type PeerSystemMeta struct {
UIVersion string UIVersion string
} }
func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool {
return p.Hostname == other.Hostname &&
p.GoOS == other.GoOS &&
p.Kernel == other.Kernel &&
p.Core == other.Core &&
p.Platform == other.Platform &&
p.OS == other.OS &&
p.WtVersion == other.WtVersion &&
p.UIVersion == other.UIVersion
}
type PeerStatus struct { type PeerStatus struct {
// LastSeen is the last time peer was connected to the management service // LastSeen is the last time peer was connected to the management service
LastSeen time.Time LastSeen time.Time
@@ -114,13 +125,19 @@ func (p *Peer) Copy() *Peer {
} }
} }
// UpdateMeta updates peer's system meta data // UpdateMetaIfNew updates peer's system metadata if new information is provided
func (p *Peer) UpdateMeta(meta PeerSystemMeta) { // returns true if meta was updated, false otherwise
func (p *Peer) UpdateMetaIfNew(meta PeerSystemMeta) bool {
// Avoid overwriting UIVersion if the update was triggered sole by the CLI client // Avoid overwriting UIVersion if the update was triggered sole by the CLI client
if meta.UIVersion == "" { if meta.UIVersion == "" {
meta.UIVersion = p.Meta.UIVersion meta.UIVersion = p.Meta.UIVersion
} }
if p.Meta.isEqual(meta) {
return false
}
p.Meta = meta p.Meta = meta
return true
} }
// MarkLoginExpired marks peer's status expired or not // MarkLoginExpired marks peer's status expired or not
@@ -654,6 +671,8 @@ func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*Peer, *NetworkMap,
return nil, nil, err return nil, nil, err
} }
// this flag prevents unnecessary calls to the persistent store.
shouldStoreAccount := false
updateRemotePeers := false updateRemotePeers := false
if peerLoginExpired(peer, account) { if peerLoginExpired(peer, account) {
err = checkAuth(login.UserID, peer) err = checkAuth(login.UserID, peer)
@@ -664,19 +683,26 @@ func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*Peer, *NetworkMap,
// UserID is present, meaning that JWT validation passed successfully in the API layer. // UserID is present, meaning that JWT validation passed successfully in the API layer.
updatePeerLastLogin(peer, account) updatePeerLastLogin(peer, account)
updateRemotePeers = true updateRemotePeers = true
shouldStoreAccount = true
} }
peer = updatePeerMeta(peer, login.Meta, account) peer, updated := updatePeerMeta(peer, login.Meta, account)
if updated {
shouldStoreAccount = true
}
peer, err = am.checkAndUpdatePeerSSHKey(peer, account, login.SSHKey) peer, err = am.checkAndUpdatePeerSSHKey(peer, account, login.SSHKey)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
err = am.Store.SaveAccount(account) if shouldStoreAccount {
if err != nil { err = am.Store.SaveAccount(account)
return nil, nil, err if err != nil {
return nil, nil, err
}
} }
if updateRemotePeers { if updateRemotePeers {
err = am.updateAccountPeers(account) err = am.updateAccountPeers(account)
if err != nil { if err != nil {
@@ -850,10 +876,12 @@ func (am *DefaultAccountManager) GetPeer(accountID, peerID, userID string) (*Pee
return nil, status.Errorf(status.Internal, "user %s has no access to peer %s under account %s", userID, peerID, accountID) return nil, status.Errorf(status.Internal, "user %s has no access to peer %s under account %s", userID, peerID, accountID)
} }
func updatePeerMeta(peer *Peer, meta PeerSystemMeta, account *Account) *Peer { func updatePeerMeta(peer *Peer, meta PeerSystemMeta, account *Account) (*Peer, bool) {
peer.UpdateMeta(meta) if peer.UpdateMetaIfNew(meta) {
account.UpdatePeer(peer) account.UpdatePeer(peer)
return peer return peer, true
}
return peer, false
} }
// GetPeerRules returns a list of source or destination rules of a given peer. // GetPeerRules returns a list of source or destination rules of a given peer.

View File

@@ -7,9 +7,10 @@ import (
"hash/crc32" "hash/crc32"
"time" "time"
"codeberg.org/ac/base62"
b "github.com/hashicorp/go-secure-stdlib/base62" b "github.com/hashicorp/go-secure-stdlib/base62"
"github.com/rs/xid" "github.com/rs/xid"
"github.com/netbirdio/netbird/base62"
) )
const ( const (

View File

@@ -4,11 +4,13 @@ import (
"crypto/sha256" "crypto/sha256"
b64 "encoding/base64" b64 "encoding/base64"
"hash/crc32" "hash/crc32"
"math/big"
"strings" "strings"
"testing" "testing"
"codeberg.org/ac/base62"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/netbirdio/netbird/base62"
) )
func TestPAT_GenerateToken_Hashing(t *testing.T) { func TestPAT_GenerateToken_Hashing(t *testing.T) {
@@ -33,6 +35,8 @@ func TestPAT_GenerateToken_Checksum(t *testing.T) {
secret := tokenWithoutPrefix[:len(tokenWithoutPrefix)-6] secret := tokenWithoutPrefix[:len(tokenWithoutPrefix)-6]
tokenCheckSum := tokenWithoutPrefix[len(tokenWithoutPrefix)-6:] tokenCheckSum := tokenWithoutPrefix[len(tokenWithoutPrefix)-6:]
var i big.Int
i.SetString(secret, 62)
expectedChecksum := crc32.ChecksumIEEE([]byte(secret)) expectedChecksum := crc32.ChecksumIEEE([]byte(secret))
actualChecksum, err := base62.Decode(tokenCheckSum) actualChecksum, err := base62.Decode(tokenCheckSum)
if err != nil { if err != nil {

View File

@@ -4,10 +4,11 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/route"
"github.com/rs/xid" "github.com/rs/xid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/route"
) )
const ( const (
@@ -946,7 +947,7 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
func createRouterStore(t *testing.T) (Store, error) { func createRouterStore(t *testing.T) (Store, error) {
dataDir := t.TempDir() dataDir := t.TempDir()
store, err := NewFileStore(dataDir) store, err := NewFileStore(dataDir, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -3,6 +3,10 @@ package telemetry
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"net/http"
"reflect"
"github.com/gorilla/mux" "github.com/gorilla/mux"
prometheus2 "github.com/prometheus/client_golang/prometheus" prometheus2 "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
@@ -10,9 +14,6 @@ import (
"go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/exporters/prometheus"
metric2 "go.opentelemetry.io/otel/metric" metric2 "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric"
"net"
"net/http"
"reflect"
) )
const defaultEndpoint = "/metrics" const defaultEndpoint = "/metrics"
@@ -25,6 +26,7 @@ type MockAppMetrics struct {
IDPMetricsFunc func() *IDPMetrics IDPMetricsFunc func() *IDPMetrics
HTTPMiddlewareFunc func() *HTTPMiddleware HTTPMiddlewareFunc func() *HTTPMiddleware
GRPCMetricsFunc func() *GRPCMetrics GRPCMetricsFunc func() *GRPCMetrics
StoreMetricsFunc func() *StoreMetrics
} }
// GetMeter mocks the GetMeter function of the AppMetrics interface // GetMeter mocks the GetMeter function of the AppMetrics interface
@@ -75,6 +77,14 @@ func (mock *MockAppMetrics) GRPCMetrics() *GRPCMetrics {
return nil return nil
} }
// StoreMetrics mocks the MockAppMetrics function of the StoreMetrics interface
func (mock *MockAppMetrics) StoreMetrics() *StoreMetrics {
if mock.StoreMetricsFunc != nil {
return mock.StoreMetricsFunc()
}
return nil
}
// AppMetrics is metrics interface // AppMetrics is metrics interface
type AppMetrics interface { type AppMetrics interface {
GetMeter() metric2.Meter GetMeter() metric2.Meter
@@ -83,6 +93,7 @@ type AppMetrics interface {
IDPMetrics() *IDPMetrics IDPMetrics() *IDPMetrics
HTTPMiddleware() *HTTPMiddleware HTTPMiddleware() *HTTPMiddleware
GRPCMetrics() *GRPCMetrics GRPCMetrics() *GRPCMetrics
StoreMetrics() *StoreMetrics
} }
// defaultAppMetrics are core application metrics based on OpenTelemetry https://opentelemetry.io/ // defaultAppMetrics are core application metrics based on OpenTelemetry https://opentelemetry.io/
@@ -94,6 +105,7 @@ type defaultAppMetrics struct {
idpMetrics *IDPMetrics idpMetrics *IDPMetrics
httpMiddleware *HTTPMiddleware httpMiddleware *HTTPMiddleware
grpcMetrics *GRPCMetrics grpcMetrics *GRPCMetrics
storeMetrics *StoreMetrics
} }
// IDPMetrics returns metrics for the idp package // IDPMetrics returns metrics for the idp package
@@ -111,6 +123,11 @@ func (appMetrics *defaultAppMetrics) GRPCMetrics() *GRPCMetrics {
return appMetrics.grpcMetrics return appMetrics.grpcMetrics
} }
// StoreMetrics returns metrics for the store
func (appMetrics *defaultAppMetrics) StoreMetrics() *StoreMetrics {
return appMetrics.storeMetrics
}
// Close stop application metrics HTTP handler and closes listener. // Close stop application metrics HTTP handler and closes listener.
func (appMetrics *defaultAppMetrics) Close() error { func (appMetrics *defaultAppMetrics) Close() error {
if appMetrics.listener == nil { if appMetrics.listener == nil {
@@ -171,11 +188,17 @@ func NewDefaultAppMetrics(ctx context.Context) (AppMetrics, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
grpcMetrics, err := NewGRPCMetrics(ctx, meter) grpcMetrics, err := NewGRPCMetrics(ctx, meter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
storeMetrics, err := NewStoreMetrics(ctx, meter)
if err != nil {
return nil, err
}
return &defaultAppMetrics{Meter: meter, ctx: ctx, idpMetrics: idpMetrics, httpMiddleware: middleware, return &defaultAppMetrics{Meter: meter, ctx: ctx, idpMetrics: idpMetrics, httpMiddleware: middleware,
grpcMetrics: grpcMetrics}, nil grpcMetrics: grpcMetrics, storeMetrics: storeMetrics}, nil
} }

View File

@@ -2,6 +2,8 @@ package telemetry
import ( import (
"context" "context"
"time"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/asyncint64" "go.opentelemetry.io/otel/metric/instrument/asyncint64"
@@ -15,6 +17,8 @@ type GRPCMetrics struct {
loginRequestsCounter syncint64.Counter loginRequestsCounter syncint64.Counter
getKeyRequestsCounter syncint64.Counter getKeyRequestsCounter syncint64.Counter
activeStreamsGauge asyncint64.Gauge activeStreamsGauge asyncint64.Gauge
syncRequestDuration syncint64.Histogram
loginRequestDuration syncint64.Histogram
ctx context.Context ctx context.Context
} }
@@ -38,12 +42,24 @@ func NewGRPCMetrics(ctx context.Context, meter metric.Meter) (*GRPCMetrics, erro
return nil, err return nil, err
} }
syncRequestDuration, err := meter.SyncInt64().Histogram("management.grpc.sync.request.duration.ms", instrument.WithUnit("milliseconds"))
if err != nil {
return nil, err
}
loginRequestDuration, err := meter.SyncInt64().Histogram("management.grpc.login.request.duration.ms", instrument.WithUnit("milliseconds"))
if err != nil {
return nil, err
}
return &GRPCMetrics{ return &GRPCMetrics{
meter: meter, meter: meter,
syncRequestsCounter: syncRequestsCounter, syncRequestsCounter: syncRequestsCounter,
loginRequestsCounter: loginRequestsCounter, loginRequestsCounter: loginRequestsCounter,
getKeyRequestsCounter: getKeyRequestsCounter, getKeyRequestsCounter: getKeyRequestsCounter,
activeStreamsGauge: activeStreamsGauge, activeStreamsGauge: activeStreamsGauge,
syncRequestDuration: syncRequestDuration,
loginRequestDuration: loginRequestDuration,
ctx: ctx, ctx: ctx,
}, err }, err
} }
@@ -63,6 +79,16 @@ func (grpcMetrics *GRPCMetrics) CountLoginRequest() {
grpcMetrics.loginRequestsCounter.Add(grpcMetrics.ctx, 1) grpcMetrics.loginRequestsCounter.Add(grpcMetrics.ctx, 1)
} }
// CountLoginRequestDuration counts the duration of the login gRPC requests
func (grpcMetrics *GRPCMetrics) CountLoginRequestDuration(duration time.Duration) {
grpcMetrics.loginRequestDuration.Record(grpcMetrics.ctx, duration.Milliseconds())
}
// CountSyncRequestDuration counts the duration of the sync gRPC requests
func (grpcMetrics *GRPCMetrics) CountSyncRequestDuration(duration time.Duration) {
grpcMetrics.syncRequestDuration.Record(grpcMetrics.ctx, duration.Milliseconds())
}
// RegisterConnectedStreams registers a function that collects number of active streams and feeds it to the metrics gauge. // RegisterConnectedStreams registers a function that collects number of active streams and feeds it to the metrics gauge.
func (grpcMetrics *GRPCMetrics) RegisterConnectedStreams(producer func() int64) error { func (grpcMetrics *GRPCMetrics) RegisterConnectedStreams(producer func() int64) error {
return grpcMetrics.meter.RegisterCallback( return grpcMetrics.meter.RegisterCallback(

View File

@@ -3,18 +3,22 @@ package telemetry
import ( import (
"context" "context"
"fmt" "fmt"
log "github.com/sirupsen/logrus"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
"hash/fnv" "hash/fnv"
"net/http" "net/http"
"strings" "strings"
time "time"
log "github.com/sirupsen/logrus"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
) )
const ( const (
httpRequestCounterPrefix = "management.http.request.counter" httpRequestCounterPrefix = "management.http.request.counter"
httpResponseCounterPrefix = "management.http.response.counter" httpResponseCounterPrefix = "management.http.response.counter"
httpRequestDurationPrefix = "management.http.request.duration.ms"
) )
// WrappedResponseWriter is a wrapper for http.ResponseWriter that allows the // WrappedResponseWriter is a wrapper for http.ResponseWriter that allows the
@@ -51,9 +55,9 @@ func (rw *WrappedResponseWriter) WriteHeader(code int) {
type HTTPMiddleware struct { type HTTPMiddleware struct {
meter metric.Meter meter metric.Meter
ctx context.Context ctx context.Context
// defaultEndpoint & method // all HTTP requests by endpoint & method
httpRequestCounters map[string]syncint64.Counter httpRequestCounters map[string]syncint64.Counter
// defaultEndpoint & method & status code // all HTTP responses by endpoint & method & status code
httpResponseCounters map[string]syncint64.Counter httpResponseCounters map[string]syncint64.Counter
// all HTTP requests // all HTTP requests
totalHTTPRequestsCounter syncint64.Counter totalHTTPRequestsCounter syncint64.Counter
@@ -61,6 +65,48 @@ type HTTPMiddleware struct {
totalHTTPResponseCounter syncint64.Counter totalHTTPResponseCounter syncint64.Counter
// all HTTP responses by status code // all HTTP responses by status code
totalHTTPResponseCodeCounters map[int]syncint64.Counter totalHTTPResponseCodeCounters map[int]syncint64.Counter
// all HTTP requests durations by endpoint and method
httpRequestDurations map[string]syncint64.Histogram
// all HTTP requests durations
totalHTTPRequestDuration syncint64.Histogram
}
// NewMetricsMiddleware creates a new HTTPMiddleware
func NewMetricsMiddleware(ctx context.Context, meter metric.Meter) (*HTTPMiddleware, error) {
totalHTTPRequestsCounter, err := meter.SyncInt64().Counter(
fmt.Sprintf("%s_total", httpRequestCounterPrefix),
instrument.WithUnit("1"))
if err != nil {
return nil, err
}
totalHTTPResponseCounter, err := meter.SyncInt64().Counter(
fmt.Sprintf("%s_total", httpResponseCounterPrefix),
instrument.WithUnit("1"))
if err != nil {
return nil, err
}
totalHTTPRequestDuration, err := meter.SyncInt64().Histogram(
fmt.Sprintf("%s_total", httpRequestDurationPrefix),
instrument.WithUnit("milliseconds"))
if err != nil {
return nil, err
}
return &HTTPMiddleware{
ctx: ctx,
httpRequestCounters: map[string]syncint64.Counter{},
httpResponseCounters: map[string]syncint64.Counter{},
httpRequestDurations: map[string]syncint64.Histogram{},
totalHTTPResponseCodeCounters: map[int]syncint64.Counter{},
meter: meter,
totalHTTPRequestsCounter: totalHTTPRequestsCounter,
totalHTTPResponseCounter: totalHTTPResponseCounter,
totalHTTPRequestDuration: totalHTTPRequestDuration,
},
nil
} }
// AddHTTPRequestResponseCounter adds a new meter for an HTTP defaultEndpoint and Method (GET, POST, etc) // AddHTTPRequestResponseCounter adds a new meter for an HTTP defaultEndpoint and Method (GET, POST, etc)
@@ -72,6 +118,12 @@ func (m *HTTPMiddleware) AddHTTPRequestResponseCounter(endpoint string, method s
return err return err
} }
m.httpRequestCounters[meterKey] = httpReqCounter m.httpRequestCounters[meterKey] = httpReqCounter
durationKey := getRequestDurationKey(endpoint, method)
requestDuration, err := m.meter.SyncInt64().Histogram(durationKey, instrument.WithUnit("milliseconds"))
if err != nil {
return err
}
m.httpRequestDurations[durationKey] = requestDuration
respCodes := []int{200, 204, 400, 401, 403, 404, 500, 502, 503} respCodes := []int{200, 204, 400, 401, 403, 404, 500, 502, 503}
for _, code := range respCodes { for _, code := range respCodes {
meterKey = getResponseCounterKey(endpoint, method, code) meterKey = getResponseCounterKey(endpoint, method, code)
@@ -92,38 +144,16 @@ func (m *HTTPMiddleware) AddHTTPRequestResponseCounter(endpoint string, method s
return nil return nil
} }
// NewMetricsMiddleware creates a new HTTPMiddleware
func NewMetricsMiddleware(ctx context.Context, meter metric.Meter) (*HTTPMiddleware, error) {
totalHTTPRequestsCounter, err := meter.SyncInt64().Counter(
fmt.Sprintf("%s_total", httpRequestCounterPrefix),
instrument.WithUnit("1"))
if err != nil {
return nil, err
}
totalHTTPResponseCounter, err := meter.SyncInt64().Counter(
fmt.Sprintf("%s_total", httpResponseCounterPrefix),
instrument.WithUnit("1"))
if err != nil {
return nil, err
}
return &HTTPMiddleware{
ctx: ctx,
httpRequestCounters: map[string]syncint64.Counter{},
httpResponseCounters: map[string]syncint64.Counter{},
totalHTTPResponseCodeCounters: map[int]syncint64.Counter{},
meter: meter,
totalHTTPRequestsCounter: totalHTTPRequestsCounter,
totalHTTPResponseCounter: totalHTTPResponseCounter,
},
nil
}
func getRequestCounterKey(endpoint, method string) string { func getRequestCounterKey(endpoint, method string) string {
return fmt.Sprintf("%s%s_%s", httpRequestCounterPrefix, return fmt.Sprintf("%s%s_%s", httpRequestCounterPrefix,
strings.ReplaceAll(endpoint, "/", "_"), method) strings.ReplaceAll(endpoint, "/", "_"), method)
} }
func getRequestDurationKey(endpoint, method string) string {
return fmt.Sprintf("%s%s_%s", httpRequestDurationPrefix,
strings.ReplaceAll(endpoint, "/", "_"), method)
}
func getResponseCounterKey(endpoint, method string, status int) string { func getResponseCounterKey(endpoint, method string, status int) string {
return fmt.Sprintf("%s%s_%s_%d", httpResponseCounterPrefix, return fmt.Sprintf("%s%s_%s_%d", httpResponseCounterPrefix,
strings.ReplaceAll(endpoint, "/", "_"), method, status) strings.ReplaceAll(endpoint, "/", "_"), method, status)
@@ -132,6 +162,7 @@ func getResponseCounterKey(endpoint, method string, status int) string {
// Handler logs every request and response and adds the, to metrics. // Handler logs every request and response and adds the, to metrics.
func (m *HTTPMiddleware) Handler(h http.Handler) http.Handler { func (m *HTTPMiddleware) Handler(h http.Handler) http.Handler {
fn := func(rw http.ResponseWriter, r *http.Request) { fn := func(rw http.ResponseWriter, r *http.Request) {
reqStart := time.Now()
traceID := hash(fmt.Sprintf("%v", r)) traceID := hash(fmt.Sprintf("%v", r))
log.Tracef("HTTP request %v: %v %v", traceID, r.Method, r.URL) log.Tracef("HTTP request %v: %v %v", traceID, r.Method, r.URL)
@@ -161,6 +192,20 @@ func (m *HTTPMiddleware) Handler(h http.Handler) http.Handler {
if c, ok := m.totalHTTPResponseCodeCounters[w.Status()]; ok { if c, ok := m.totalHTTPResponseCodeCounters[w.Status()]; ok {
c.Add(m.ctx, 1) c.Add(m.ctx, 1)
} }
durationKey := getRequestDurationKey(r.URL.Path, r.Method)
reqTook := time.Since(reqStart)
if c, ok := m.httpRequestDurations[durationKey]; ok {
c.Record(m.ctx, reqTook.Milliseconds())
}
log.Debugf("request %s %s took %d ms and finished with status %d", r.Method, r.URL.Path, reqTook.Milliseconds(), w.Status())
if w.Status() == 200 && (r.Method == http.MethodPut || r.Method == http.MethodPost || r.Method == http.MethodDelete) {
m.totalHTTPRequestDuration.Record(m.ctx, reqTook.Milliseconds(), attribute.String("type", "write"))
} else {
m.totalHTTPRequestDuration.Record(m.ctx, reqTook.Milliseconds(), attribute.String("type", "read"))
}
} }
return http.HandlerFunc(fn) return http.HandlerFunc(fn)

View File

@@ -0,0 +1,47 @@
package telemetry
import (
"context"
"time"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
)
// StoreMetrics represents all metrics related to the FileStore
type StoreMetrics struct {
globalLockAcquisitionDuration syncint64.Histogram
persistenceDuration syncint64.Histogram
ctx context.Context
}
// NewStoreMetrics creates an instance of StoreMetrics
func NewStoreMetrics(ctx context.Context, meter metric.Meter) (*StoreMetrics, error) {
globalLockAcquisitionDuration, err := meter.SyncInt64().Histogram("management.store.global.lock.acquisition.duration.micro",
instrument.WithUnit("microseconds"))
if err != nil {
return nil, err
}
persistenceDuration, err := meter.SyncInt64().Histogram("management.store.persistence.duration.micro",
instrument.WithUnit("microseconds"))
if err != nil {
return nil, err
}
return &StoreMetrics{
globalLockAcquisitionDuration: globalLockAcquisitionDuration,
persistenceDuration: persistenceDuration,
ctx: ctx,
}, nil
}
// CountGlobalLockAcquisitionDuration counts the duration of the global lock acquisition
func (metrics *StoreMetrics) CountGlobalLockAcquisitionDuration(duration time.Duration) {
metrics.globalLockAcquisitionDuration.Record(metrics.ctx, duration.Microseconds())
}
// CountPersistenceDuration counts the duration of a store persistence operation
func (metrics *StoreMetrics) CountPersistenceDuration(duration time.Duration) {
metrics.persistenceDuration.Record(metrics.ctx, duration.Microseconds())
}

View File

@@ -565,6 +565,14 @@ func (am *DefaultAccountManager) SaveUser(accountID, initiatorUserID string, upd
} }
defer func() { defer func() {
if oldUser.IsBlocked() != update.IsBlocked() {
if update.IsBlocked() {
am.storeEvent(initiatorUserID, oldUser.Id, accountID, activity.UserBlocked, nil)
} else {
am.storeEvent(initiatorUserID, oldUser.Id, accountID, activity.UserUnblocked, nil)
}
}
// store activity logs // store activity logs
if oldUser.Role != newUser.Role { if oldUser.Role != newUser.Role {
am.storeEvent(initiatorUserID, oldUser.Id, accountID, activity.UserRoleUpdated, map[string]any{"role": newUser.Role}) am.storeEvent(initiatorUserID, oldUser.Id, accountID, activity.UserRoleUpdated, map[string]any{"role": newUser.Role})

View File

@@ -0,0 +1,34 @@
#!/bin/sh
APP=/Applications/NetBird.app
AGENT=/usr/local/bin/netbird
LOG_FILE=/var/log/netbird/client_install.log
mkdir -p $LOG_FILE
{
echo "Installing NetBird..."
if test -d $APP; then
echo "NetBird app copied successfully."
else
echo "NetBird app could not be copied to the Applications folder."
exit 1
fi
ln -s $APP/Contents/MacOS/netbird $AGENT
if test -f $AGENT; then
echo "NetBird binary linked successfully."
else
echo "NetBird could not create symlink to /usr/local/bin"
exit 1
fi
$AGENT service install
$AGENT service start
open $APP
echo "Finished Netbird installation successfully"
exit 0 # all good
} &> $LOG_FILE

View File

@@ -0,0 +1,10 @@
#!/bin/sh
LOG_FILE=/var/log/netbird/client_install.log
mkdir -p $LOG_FILE
{
echo "Preinstall complete"
exit 0 # all good
} &> $LOG_FILE

View File

@@ -24,6 +24,8 @@ import (
"github.com/netbirdio/netbird/signal/proto" "github.com/netbirdio/netbird/signal/proto"
) )
const defaultSendTimeout = 5 * time.Second
// ConnStateNotifier is a wrapper interface of the status recorder // ConnStateNotifier is a wrapper interface of the status recorder
type ConnStateNotifier interface { type ConnStateNotifier interface {
MarkSignalDisconnected() MarkSignalDisconnected()
@@ -322,14 +324,28 @@ func (c *GrpcClient) Send(msg *proto.Message) error {
return err return err
} }
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) attemptTimeout := defaultSendTimeout
defer cancel()
_, err = c.realClient.Send(ctx, encryptedMessage) for attempt := 0; attempt < 4; attempt++ {
if err != nil { if attempt > 1 {
return err attemptTimeout = time.Duration(attempt) * 5 * time.Second
}
ctx, cancel := context.WithTimeout(c.ctx, attemptTimeout)
_, err = c.realClient.Send(ctx, encryptedMessage)
cancel()
if s, ok := status.FromError(err); ok && s.Code() == codes.Canceled {
return err
}
if err == nil {
return nil
}
} }
return nil return err
} }
// receive receives messages from other peers coming through the Signal Exchange // receive receives messages from other peers coming through the Signal Exchange