Compare commits

..

5 Commits

Author SHA1 Message Date
Viktor Liu
1e630b5d45 Fix signature file being gzipped 2024-02-27 11:45:44 +01:00
Viktor Liu
7251150c1c Combine update-available and connected/disconnected tray icon states (#1615)
This PR updates the system tray icons to reflect both connection status and availability of updates. Now, the tray will show distinct icons for the following states: connected, disconnected, update available while connected, and update available while disconnected. This change improves user experience by providing a clear visual status indicator.

- Add new icons for connected and disconnected states with update available.
- Implement logic to switch icons based on connection status and update availability.
- Remove old icon references for default and update states.
2024-02-26 23:28:33 +01:00
Bethuel Mmbaga
b65c2f69b0 Add support for downloading Geo databases to the management service (#1626)
Adds support for downloading Geo databases to the management service. If the Geo databases are not found, the service will automatically attempt to download them during startup.
2024-02-26 22:49:28 +01:00
Yury Gargay
d8ce08d898 Extend bypass middleware with support of wildcard paths (#1628)
---------

Co-authored-by: Viktor Liu <viktor@netbird.io>
2024-02-26 17:54:58 +01:00
Maycon Santos
e1c50248d9 Add support for device flow on getting started with zitadel (#1616) 2024-02-26 12:33:16 +01:00
58 changed files with 745 additions and 1545 deletions

View File

@@ -2,7 +2,7 @@
name: Bug/Issue report
about: Create a report to help us improve
title: ''
labels: ['triage-needed']
labels: ['triage']
assignees: ''
---

View File

@@ -162,13 +162,6 @@ jobs:
test $count -eq 4
working-directory: infrastructure_files/artifacts
- name: test geolocation databases
working-directory: infrastructure_files/artifacts
run: |
sleep 30
docker compose exec management ls -l /var/lib/netbird/ | grep -i GeoLite2-City.mmdb
docker compose exec management ls -l /var/lib/netbird/ | grep -i geonames.db
test-getting-started-script:
runs-on: ubuntu-latest
steps:

View File

@@ -63,14 +63,6 @@ linters-settings:
enable:
- nilness
revive:
rules:
- name: exported
severity: warning
disabled: false
arguments:
- "checkPrivateReceivers"
- "sayRepetitiveInsteadOfStutters"
tenv:
# The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
# Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
@@ -101,7 +93,6 @@ linters:
- nilerr # finds the code that returns nil even if it checks that the error is not nil
- nilnil # checks that there is no simultaneous return of nil error and an invalid value
- predeclared # predeclared finds code that shadows one of Go's predeclared identifiers
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
- thelper # thelper detects Go test helpers without t.Helper() call and checks the consistency of test helpers.
- wastedassign # wastedassign finds wasted assignment statements

View File

@@ -1,6 +1,6 @@
<p align="center">
<strong>:hatching_chick: New Release! Device Posture Checks.</strong>
<a href="https://docs.netbird.io/how-to/manage-posture-checks">
<strong>:hatching_chick: New Release! Self-hosting in under 5 min.</strong>
<a href="https://github.com/netbirdio/netbird#quickstart-with-self-hosted-netbird">
Learn more
</a>
</p>
@@ -42,22 +42,25 @@
**Secure.** NetBird enables secure remote access by applying granular access policies, while allowing you to manage them intuitively from a single place. Works universally on any infrastructure.
### Open-Source Network Security in a Single Platform
### Secure peer-to-peer VPN with SSO and MFA in minutes
![download (2)](https://github.com/netbirdio/netbird/assets/700848/16210ac2-7265-44c1-8d4e-8fae85534dac)
https://user-images.githubusercontent.com/700848/197345890-2e2cded5-7b7a-436f-a444-94e80dd24f46.mov
### Key features
| Connectivity | Management | Security | Automation | Platforms |
|------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
| <ul><li> - \[x] Kernel WireGuard </ul></li> | <ul><li> - \[x] [Admin Web UI](https://github.com/netbirdio/dashboard) </ul></li> | <ul><li> - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login) </ul></li> | <ul><li> - \[x] [Public API](https://docs.netbird.io/api) </ul></li> | <ul><li> - \[x] Linux </ul></li> |
| <ul><li> - \[x] Peer-to-peer connections </ul></li> | <ul><li> - \[x] Auto peer discovery and configuration </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | <ul><li> - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys) </ul></li> | <ul><li> - \[x] Mac </ul></li> |
| <ul><li> - \[x] Connection relay fallback </ul></li> | <ul><li> - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers) </ul></li> | <ul><li> - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity) </ul></li> | <ul><li> - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart) </ul></li> | <ul><li> - \[x] Windows </ul></li> |
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network) </ul></li> | <ul><li> - \[x] [Device posture checks](https://docs.netbird.io/how-to/manage-posture-checks) </ul></li> | <ul><li> - \[x] IdP groups sync with JWT </ul></li> | <ul><li> - \[x] Android </ul></li> |
| <ul><li> - \[x] NAT traversal with BPF </ul></li> | <ul><li> - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network) </ul></li> | <ul><li> - \[x] Peer-to-peer encryption </ul></li> | | <ul><li> - \[x] iOS </ul></li> |
| | | <ul><li> - \[x] [Quantum-resistance with Rosenpass](https://netbird.io/knowledge-hub/the-first-quantum-resistant-mesh-vpn) </ul></li> | | <ul><li> - \[x] OpenWRT </ul></li> |
| | | <ui><li> - \[x] [Periodic re-authentication](https://docs.netbird.io/how-to/enforce-periodic-user-authentication)</ul></li> | | <ul><li> - \[x] [Serverless](https://docs.netbird.io/how-to/netbird-on-faas) </ul></li> |
| | | | | <ul><li> - \[x] Docker </ul></li> |
| Connectivity | Management | Automation | Platforms |
|---------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|----------------------------------------------------------------------------|---------------------------------------|
| <ul><li> - \[x] Kernel WireGuard </ul></li> | <ul><li> - \[x] [Admin Web UI](https://github.com/netbirdio/dashboard) </ul></li> | <ul><li> - \[x] [Public API](https://docs.netbird.io/api) </ul></li> | <ul><li> - \[x] Linux </ul></li> |
| <ul><li> - \[x] Peer-to-peer connections </ul></li> | <ul><li> - \[x] Auto peer discovery and configuration </ul></li> | <ul><li> - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys) </ul></li> | <ul><li> - \[x] Mac </ul></li> |
| <ul><li> - \[x] Peer-to-peer encryption </ul></li> | <ul><li> - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers) </ul></li> | <ul><li> - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart) </ul></li> | <ul><li> - \[x] Windows </ul></li> |
| <ul><li> - \[x] Connection relay fallback </ul></li> | <ul><li> - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login) </ul></li> | <ul><li> - \[x] IdP groups sync with JWT </ul></li> | <ul><li> - \[x] Android </ul></li> |
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | | <ul><li> - \[x] iOS </ul></li> |
| <ul><li> - \[x] NAT traversal with BPF </ul></li> | <ul><li> - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network) </ul></li> | | <ul><li> - \[x] Docker </ul></li> |
| <ul><li> - \[x] Post-quantum-secure connection through [Rosenpass](https://rosenpass.eu) </ul></li> | <ul><li> - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network) </ul></li> | | <ul><li> - \[x] OpenWRT </ul></li> |
| | <ul><li> - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity) </ul></li> | | |
| | <ul><li> - \[x] SSH access management </ul></li> | | |
### Quickstart with NetBird Cloud
- Download and install NetBird at [https://app.netbird.io/install](https://app.netbird.io/install)

View File

@@ -13,7 +13,6 @@ import (
"google.golang.org/grpc"
"github.com/netbirdio/management-integrations/integrations"
clientProto "github.com/netbirdio/netbird/client/proto"
client "github.com/netbirdio/netbird/client/server"
mgmtProto "github.com/netbirdio/netbird/management/proto"
@@ -76,8 +75,10 @@ func startManagement(t *testing.T, config *mgmt.Config) (*grpc.Server, net.Liste
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
iv, _ := integrations.NewIntegratedApproval(eventStore)
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false, iv)
if err != nil {
return nil, nil
}
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false)
if err != nil {
t.Fatal(err)
}

View File

@@ -26,7 +26,7 @@ type HTTPClient interface {
}
// AuthFlowInfo holds information for the OAuth 2.0 authorization flow
type AuthFlowInfo struct { //nolint:revive
type AuthFlowInfo struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURI string `json:"verification_uri"`

View File

@@ -8,7 +8,6 @@ import (
"net/netip"
"os"
"strings"
"time"
log "github.com/sirupsen/logrus"
)
@@ -24,16 +23,10 @@ const (
fileMaxNumberOfSearchDomains = 6
)
const (
dnsFailoverTimeout = 4 * time.Second
dnsFailoverAttempts = 1
)
type fileConfigurator struct {
repair *repair
originalPerms os.FileMode
nbNameserverIP string
originalPerms os.FileMode
}
func newFileConfigurator() (hostManager, error) {
@@ -71,7 +64,7 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
}
nbSearchDomains := searchDomains(config)
f.nbNameserverIP = config.ServerIP
nbNameserverIP := config.ServerIP
resolvConf, err := parseBackupResolvConf()
if err != nil {
@@ -80,11 +73,11 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
f.repair.stopWatchFileChanges()
err = f.updateConfig(nbSearchDomains, f.nbNameserverIP, resolvConf)
err = f.updateConfig(nbSearchDomains, nbNameserverIP, resolvConf)
if err != nil {
return err
}
f.repair.watchFileChanges(nbSearchDomains, f.nbNameserverIP)
f.repair.watchFileChanges(nbSearchDomains, nbNameserverIP)
return nil
}
@@ -92,11 +85,10 @@ func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP
searchDomainList := mergeSearchDomains(nbSearchDomains, cfg.searchDomains)
nameServers := generateNsList(nbNameserverIP, cfg)
options := prepareOptionsWithTimeout(cfg.others, int(dnsFailoverTimeout.Seconds()), dnsFailoverAttempts)
buf := prepareResolvConfContent(
searchDomainList,
nameServers,
options)
cfg.others)
log.Debugf("creating managed file %s", defaultResolvConfPath)
err := os.WriteFile(defaultResolvConfPath, buf.Bytes(), f.originalPerms)
@@ -139,12 +131,7 @@ func (f *fileConfigurator) backup() error {
}
func (f *fileConfigurator) restore() error {
err := removeFirstNbNameserver(fileDefaultResolvConfBackupLocation, f.nbNameserverIP)
if err != nil {
log.Errorf("Failed to remove netbird nameserver from %s on backup restore: %s", fileDefaultResolvConfBackupLocation, err)
}
err = copyFile(fileDefaultResolvConfBackupLocation, defaultResolvConfPath)
err := copyFile(fileDefaultResolvConfBackupLocation, defaultResolvConfPath)
if err != nil {
return fmt.Errorf("restoring %s from %s: %w", defaultResolvConfPath, fileDefaultResolvConfBackupLocation, err)
}
@@ -170,7 +157,7 @@ func (f *fileConfigurator) restoreUncleanShutdownDNS(storedDNSAddress *netip.Add
currentDNSAddress, err := netip.ParseAddr(resolvConf.nameServers[0])
// not a valid first nameserver -> restore
if err != nil {
log.Errorf("restoring unclean shutdown: parse dns address %s failed: %s", resolvConf.nameServers[0], err)
log.Errorf("restoring unclean shutdown: parse dns address %s failed: %s", resolvConf.nameServers[1], err)
return restoreResolvConfFile()
}

View File

@@ -5,7 +5,6 @@ package dns
import (
"fmt"
"os"
"regexp"
"strings"
log "github.com/sirupsen/logrus"
@@ -15,9 +14,6 @@ const (
defaultResolvConfPath = "/etc/resolv.conf"
)
var timeoutRegex = regexp.MustCompile(`timeout:\d+`)
var attemptsRegex = regexp.MustCompile(`attempts:\d+`)
type resolvConf struct {
nameServers []string
searchDomains []string
@@ -107,62 +103,3 @@ func parseResolvConfFile(resolvConfFile string) (*resolvConf, error) {
}
return rconf, nil
}
// prepareOptionsWithTimeout appends timeout to existing options if it doesn't exist,
// otherwise it adds a new option with timeout and attempts.
func prepareOptionsWithTimeout(input []string, timeout int, attempts int) []string {
configs := make([]string, len(input))
copy(configs, input)
for i, config := range configs {
if strings.HasPrefix(config, "options") {
config = strings.ReplaceAll(config, "rotate", "")
config = strings.Join(strings.Fields(config), " ")
if strings.Contains(config, "timeout:") {
config = timeoutRegex.ReplaceAllString(config, fmt.Sprintf("timeout:%d", timeout))
} else {
config = strings.Replace(config, "options ", fmt.Sprintf("options timeout:%d ", timeout), 1)
}
if strings.Contains(config, "attempts:") {
config = attemptsRegex.ReplaceAllString(config, fmt.Sprintf("attempts:%d", attempts))
} else {
config = strings.Replace(config, "options ", fmt.Sprintf("options attempts:%d ", attempts), 1)
}
configs[i] = config
return configs
}
}
return append(configs, fmt.Sprintf("options timeout:%d attempts:%d", timeout, attempts))
}
// removeFirstNbNameserver removes the given nameserver from the given file if it is in the first position
// and writes the file back to the original location
func removeFirstNbNameserver(filename, nameserverIP string) error {
resolvConf, err := parseResolvConfFile(filename)
if err != nil {
return fmt.Errorf("parse backup resolv.conf: %w", err)
}
content, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("read %s: %w", filename, err)
}
if len(resolvConf.nameServers) > 1 && resolvConf.nameServers[0] == nameserverIP {
newContent := strings.Replace(string(content), fmt.Sprintf("nameserver %s\n", nameserverIP), "", 1)
stat, err := os.Stat(filename)
if err != nil {
return fmt.Errorf("stat %s: %w", filename, err)
}
if err := os.WriteFile(filename, []byte(newContent), stat.Mode()); err != nil {
return fmt.Errorf("write %s: %w", filename, err)
}
}
return nil
}

View File

@@ -6,8 +6,6 @@ import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_parseResolvConf(t *testing.T) {
@@ -174,131 +172,3 @@ nameserver 192.168.0.1
t.Errorf("unexpected resolv.conf content: %v", cfg)
}
}
func TestPrepareOptionsWithTimeout(t *testing.T) {
tests := []struct {
name string
others []string
timeout int
attempts int
expected []string
}{
{
name: "Append new options with timeout and attempts",
others: []string{"some config"},
timeout: 2,
attempts: 2,
expected: []string{"some config", "options timeout:2 attempts:2"},
},
{
name: "Modify existing options to exclude rotate and include timeout and attempts",
others: []string{"some config", "options rotate someother"},
timeout: 3,
attempts: 2,
expected: []string{"some config", "options attempts:2 timeout:3 someother"},
},
{
name: "Existing options with timeout and attempts are updated",
others: []string{"some config", "options timeout:4 attempts:3"},
timeout: 5,
attempts: 4,
expected: []string{"some config", "options timeout:5 attempts:4"},
},
{
name: "Modify existing options, add missing attempts before timeout",
others: []string{"some config", "options timeout:4"},
timeout: 4,
attempts: 3,
expected: []string{"some config", "options attempts:3 timeout:4"},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := prepareOptionsWithTimeout(tc.others, tc.timeout, tc.attempts)
assert.Equal(t, tc.expected, result)
})
}
}
func TestRemoveFirstNbNameserver(t *testing.T) {
testCases := []struct {
name string
content string
ipToRemove string
expected string
}{
{
name: "Unrelated nameservers with comments and options",
content: `# This is a comment
options rotate
nameserver 1.1.1.1
# Another comment
nameserver 8.8.4.4
search example.com`,
ipToRemove: "9.9.9.9",
expected: `# This is a comment
options rotate
nameserver 1.1.1.1
# Another comment
nameserver 8.8.4.4
search example.com`,
},
{
name: "First nameserver matches",
content: `search example.com
nameserver 9.9.9.9
# oof, a comment
nameserver 8.8.4.4
options attempts:5`,
ipToRemove: "9.9.9.9",
expected: `search example.com
# oof, a comment
nameserver 8.8.4.4
options attempts:5`,
},
{
name: "Target IP not the first nameserver",
// nolint:dupword
content: `# Comment about the first nameserver
nameserver 8.8.4.4
# Comment before our target
nameserver 9.9.9.9
options timeout:2`,
ipToRemove: "9.9.9.9",
// nolint:dupword
expected: `# Comment about the first nameserver
nameserver 8.8.4.4
# Comment before our target
nameserver 9.9.9.9
options timeout:2`,
},
{
name: "Only nameserver matches",
content: `options debug
nameserver 9.9.9.9
search localdomain`,
ipToRemove: "9.9.9.9",
expected: `options debug
nameserver 9.9.9.9
search localdomain`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tempDir := t.TempDir()
tempFile := filepath.Join(tempDir, "resolv.conf")
err := os.WriteFile(tempFile, []byte(tc.content), 0644)
assert.NoError(t, err)
err = removeFirstNbNameserver(tempFile, tc.ipToRemove)
assert.NoError(t, err)
content, err := os.ReadFile(tempFile)
assert.NoError(t, err)
assert.Equal(t, tc.expected, string(content), "The resulting content should match the expected output.")
})
}
}

View File

@@ -65,7 +65,7 @@ func newHostManager(wgInterface string) (hostManager, error) {
return nil, err
}
log.Infof("System DNS manager discovered: %s", osManager)
log.Debugf("discovered mode is: %s", osManager)
return newHostManagerFromType(wgInterface, osManager)
}

View File

@@ -53,12 +53,10 @@ func (r *resolvconf) applyDNSConfig(config HostDNSConfig) error {
searchDomainList := searchDomains(config)
searchDomainList = mergeSearchDomains(searchDomainList, r.originalSearchDomains)
options := prepareOptionsWithTimeout(r.othersConfigs, int(dnsFailoverTimeout.Seconds()), dnsFailoverAttempts)
buf := prepareResolvConfContent(
searchDomainList,
append([]string{config.ServerIP}, r.originalNameServers...),
options)
r.othersConfigs)
// create a backup for unclean shutdown detection before the resolv.conf is changed
if err := createUncleanShutdownIndicator(defaultResolvConfPath, resolvConfManager, config.ServerIP); err != nil {

View File

@@ -21,7 +21,6 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/client/internal/dns"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/routemanager"
@@ -71,10 +70,10 @@ func TestEngine_SSH(t *testing.T) {
defer cancel()
engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, &EngineConfig{
WgIfaceName: "utun101",
WgAddr: "100.64.0.1/24",
WgPrivateKey: key,
WgPort: 33100,
WgIfaceName: "utun101",
WgAddr: "100.64.0.1/24",
WgPrivateKey: key,
WgPort: 33100,
ServerSSHAllowed: true,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
@@ -1048,8 +1047,10 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
peersUpdateManager := server.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
ia, _ := integrations.NewIntegratedApproval(eventStore)
accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false, ia)
if err != nil {
return nil, "", err
}
accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false)
if err != nil {
return nil, "", err
}

View File

@@ -61,7 +61,7 @@ func main() {
flag.Parse()
a := app.NewWithID("NetBird")
a := app.New()
a.SetIcon(fyne.NewStaticResource("netbird", iconDisconnectedPNG))
client := newServiceClient(daemonAddr, a, showSettings)
@@ -130,10 +130,9 @@ type serviceClient struct {
mQuit *systray.MenuItem
// application with main windows.
app fyne.App
wSettings fyne.Window
showSettings bool
sendNotification bool
app fyne.App
wSettings fyne.Window
showSettings bool
// input elements for settings form
iMngURL *widget.Entry
@@ -159,10 +158,9 @@ type serviceClient struct {
// This constructor also builds the UI elements for the settings window.
func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient {
s := &serviceClient{
ctx: context.Background(),
addr: addr,
app: a,
sendNotification: false,
ctx: context.Background(),
addr: addr,
app: a,
showSettings: showSettings,
update: version.NewUpdate(),
@@ -379,15 +377,9 @@ func (s *serviceClient) updateStatus() error {
s.updateIndicationLock.Lock()
defer s.updateIndicationLock.Unlock()
// notify the user when the session has expired
if status.Status == string(internal.StatusNeedsLogin) {
s.onSessionExpire()
}
var systrayIconState bool
if status.Status == string(internal.StatusConnected) && !s.mUp.Disabled() {
s.connected = true
s.sendNotification = true
if s.isUpdateIconActive {
systray.SetIcon(s.icUpdateConnected)
} else {
@@ -638,23 +630,6 @@ func (s *serviceClient) onUpdateAvailable() {
}
}
// onSessionExpire sends a notification to the user when the session expires.
func (s *serviceClient) onSessionExpire() {
if s.sendNotification {
title := "Connection session expired"
if runtime.GOOS == "darwin" {
title = "NetBird connection session expired"
}
s.app.SendNotification(
fyne.NewNotification(
title,
"Please re-authenticate to connect to the network",
),
)
s.sendNotification = false
}
}
func openURL(url string) error {
var err error
switch runtime.GOOS {

5
go.mod
View File

@@ -46,7 +46,6 @@ require (
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.9
github.com/google/gopacket v1.1.19
github.com/google/martian/v3 v3.0.0
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240212192251-757544f21357
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2
@@ -58,8 +57,8 @@ require (
github.com/miekg/dns v1.1.43
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/nadoo/ipset v0.5.0
github.com/netbirdio/management-integrations/additions v0.0.0-20240226151841-2e4fe2407450
github.com/netbirdio/management-integrations/integrations v0.0.0-20240305130559-469a80446ac7
github.com/netbirdio/management-integrations/additions v0.0.0-20240212121739-8ea8c89a4552
github.com/netbirdio/management-integrations/integrations v0.0.0-20240212121739-8ea8c89a4552
github.com/okta/okta-sdk-golang/v2 v2.18.0
github.com/oschwald/maxminddb-golang v1.12.0
github.com/patrickmn/go-cache v2.1.0+incompatible

9
go.sum
View File

@@ -255,7 +255,6 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/nftables v0.0.0-20220808154552-2eca00135732 h1:csc7dT82JiSLvq4aMyQMIQDL7986NH6Wxf/QrvOj55A=
github.com/google/nftables v0.0.0-20220808154552-2eca00135732/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc=
@@ -377,10 +376,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
github.com/netbirdio/management-integrations/additions v0.0.0-20240226151841-2e4fe2407450 h1:qA4S5YFt6/s0kQ8wKLjq8faLxuBSte1WzjWfmQmyJTU=
github.com/netbirdio/management-integrations/additions v0.0.0-20240226151841-2e4fe2407450/go.mod h1:31FhBNvQ+riHEIu6LSTmqr8IeuSIsGfQffqV4LFmbwA=
github.com/netbirdio/management-integrations/integrations v0.0.0-20240305130559-469a80446ac7 h1:YYIQJbRhANmNFClkCmjBa0w33RpTzsF2DpbGAWhul6Y=
github.com/netbirdio/management-integrations/integrations v0.0.0-20240305130559-469a80446ac7/go.mod h1:B0nMS3es77gOvPYhc0K91fAzTkQLi/jRq5TffUN3klM=
github.com/netbirdio/management-integrations/additions v0.0.0-20240212121739-8ea8c89a4552 h1:yzcQKizAK9YufCHMMCIsr467Dw/OU/4xyHbWizGb1E4=
github.com/netbirdio/management-integrations/additions v0.0.0-20240212121739-8ea8c89a4552/go.mod h1:31FhBNvQ+riHEIu6LSTmqr8IeuSIsGfQffqV4LFmbwA=
github.com/netbirdio/management-integrations/integrations v0.0.0-20240212121739-8ea8c89a4552 h1:OFlzVZtkXCoJsfDKrMigFpuad8ZXTm8epq6x27K0irA=
github.com/netbirdio/management-integrations/integrations v0.0.0-20240212121739-8ea8c89a4552/go.mod h1:B0nMS3es77gOvPYhc0K91fAzTkQLi/jRq5TffUN3klM=
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0 h1:hirFRfx3grVA/9eEyjME5/z3nxdJlN9kfQpvWWPk32g=
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949 h1:xbWM9BU6mwZZLHxEjxIX/V8Hv3HurQt4mReIE4mY4DM=

View File

@@ -8,7 +8,7 @@ import (
"golang.zx2c4.com/wireguard/tun/netstack"
)
type NetStackTun struct { //nolint:revive
type NetStackTun struct {
address string
mtu int
listenAddress string

View File

@@ -30,6 +30,9 @@ download_geolite_mmdb() {
# Verify the signature
echo "Verifying signature..."
mv "$SIGNATURE_FILE" "$SIGNATURE_FILE".gz
gunzip "$SIGNATURE_FILE".gz
if sha256sum -c --status "$SIGNATURE_FILE"; then
echo "Signature is valid."
else
@@ -71,6 +74,8 @@ download_geolite_csv_and_create_sqlite_db() {
# Verify the signature
echo "Verifying signature..."
mv "$SIGNATURE_FILE" "$SIGNATURE_FILE".gz
gunzip "$SIGNATURE_FILE".gz
if sha256sum -c --status "$SIGNATURE_FILE"; then
echo "Signature is valid."
else

View File

@@ -26,13 +26,6 @@
"Username": "",
"Password": null
},
"ReverseProxy": {
"TrustedHTTPProxies": [],
"TrustedHTTPProxiesCount": 0,
"TrustedPeers": [
"0.0.0.0/0"
]
},
"Datadir": "",
"DataStoreEncryptionKey": "$NETBIRD_DATASTORE_ENC_KEY",
"StoreConfig": {

View File

@@ -46,7 +46,6 @@ server {
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Proxy dashboard
location / {

View File

@@ -15,7 +15,6 @@ import (
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/encryption"
mgmtProto "github.com/netbirdio/netbird/management/proto"
mgmt "github.com/netbirdio/netbird/management/server"
@@ -61,8 +60,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
ia, _ := integrations.NewIntegratedApproval(eventStore)
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false, ia)
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false)
if err != nil {
t.Fatal(err)
}
@@ -365,11 +363,10 @@ func Test_SystemMetaDataFromClient(t *testing.T) {
WiretrusteeVersion: info.WiretrusteeVersion,
KernelVersion: info.KernelVersion,
NetworkAddresses: protoNetAddr,
SysSerialNumber: info.SystemSerialNumber,
SysProductName: info.SystemProductName,
SysManufacturer: info.SystemManufacturer,
Environment: &mgmtProto.Environment{Cloud: info.Environment.Cloud, Platform: info.Environment.Platform},
NetworkAddresses: protoNetAddr,
SysSerialNumber: info.SystemSerialNumber,
SysProductName: info.SystemProductName,
SysManufacturer: info.SystemManufacturer,
}
assert.Equal(t, ValidKey, actualValidKey)
@@ -410,9 +407,7 @@ func isEqual(a, b *mgmtProto.PeerSystemMeta) bool {
a.GetUiVersion() == b.GetUiVersion() &&
a.GetSysSerialNumber() == b.GetSysSerialNumber() &&
a.GetSysProductName() == b.GetSysProductName() &&
a.GetSysManufacturer() == b.GetSysManufacturer() &&
a.GetEnvironment().Cloud == b.GetEnvironment().Cloud &&
a.GetEnvironment().Platform == b.GetEnvironment().Platform
a.GetSysManufacturer() == b.GetSysManufacturer()
}
func Test_GetDeviceAuthorizationFlow(t *testing.T) {

View File

@@ -26,8 +26,6 @@ import (
"github.com/netbirdio/netbird/management/proto"
)
const ConnectTimeout = 10 * time.Second
// ConnStateNotifier is a wrapper interface of the status recorders
type ConnStateNotifier interface {
MarkManagementDisconnected(error)
@@ -51,7 +49,7 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE
transportOption = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))
}
mgmCtx, cancel := context.WithTimeout(ctx, ConnectTimeout)
mgmCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
conn, err := grpc.DialContext(
mgmCtx,
@@ -320,7 +318,7 @@ func (c *GrpcClient) login(serverKey wgtypes.Key, req *proto.LoginRequest) (*pro
log.Errorf("failed to encrypt message: %s", err)
return nil, err
}
mgmCtx, cancel := context.WithTimeout(c.ctx, ConnectTimeout)
mgmCtx, cancel := context.WithTimeout(c.ctx, 5*time.Second)
defer cancel()
resp, err := c.realClient.Login(mgmCtx, &proto.EncryptedMessage{
WgPubKey: c.key.PublicKey().String(),
@@ -476,9 +474,5 @@ func infoToMetaData(info *system.Info) *proto.PeerSystemMeta {
SysSerialNumber: info.SystemSerialNumber,
SysManufacturer: info.SystemManufacturer,
SysProductName: info.SystemProductName,
Environment: &proto.Environment{
Cloud: info.Environment.Cloud,
Platform: info.Environment.Platform,
},
}
}

View File

@@ -43,7 +43,6 @@ import (
"github.com/netbirdio/netbird/management/server/metrics"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/util"
"github.com/netbirdio/netbird/version"
)
// ManagementLegacyPort is the port that was used before by the Management gRPC server.
@@ -172,12 +171,8 @@ var (
log.Infof("geo location service has been initialized from %s", config.Datadir)
}
integratedPeerApproval, err := integrations.NewIntegratedApproval(eventStore)
if err != nil {
return fmt.Errorf("failed to initialize integrated peer approval: %v", err)
}
accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain,
dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerApproval)
dnsDomain, eventStore, geo, userDeleteFromIDPEnabled)
if err != nil {
return fmt.Errorf("failed to build default manager: %v", err)
}
@@ -320,14 +315,12 @@ var (
}
}
log.Infof("management server version %s", version.NetbirdVersion())
log.Infof("running HTTP server and gRPC server on the same port: %s", listener.Addr().String())
serveGRPCWithHTTP(listener, rootHandler, tlsEnabled)
SetupCloseHandler()
<-stopCh
integratedPeerApproval.Stop()
if geo != nil {
_ = geo.Stop()
}

File diff suppressed because it is too large Load Diff

View File

@@ -92,14 +92,6 @@ message PeerKeys {
bytes wgPubKey = 2;
}
// Environment is part of the PeerSystemMeta and describes the environment the agent is running in.
message Environment {
// cloud is the cloud provider the agent is running in if applicable.
string cloud = 1;
// platform is the platform the agent is running on if applicable.
string platform = 2;
}
// PeerSystemMeta is machine meta data like OS and version.
message PeerSystemMeta {
string hostname = 1;
@@ -116,7 +108,6 @@ message PeerSystemMeta {
string sysSerialNumber = 12;
string sysProductName = 13;
string sysManufacturer = 14;
Environment environment = 15;
}
message LoginResponse {

View File

@@ -22,13 +22,13 @@ import (
log "github.com/sirupsen/logrus"
"github.com/netbirdio/management-integrations/additions"
"github.com/netbirdio/netbird/base62"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/integrated_approval"
"github.com/netbirdio/netbird/management/server/jwtclaims"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
@@ -126,9 +126,6 @@ type AccountManager interface {
SavePostureChecks(accountID, userID string, postureChecks *posture.Checks) error
DeletePostureChecks(accountID, postureChecksID, userID string) error
ListPostureChecks(accountID, userID string) ([]*posture.Checks, error)
GetIdpManager() idp.Manager
UpdateIntegratedApprovalGroups(accountID string, userID string, groups []string) error
GroupValidation(accountId string, groups []string) (bool, error)
}
type DefaultAccountManager struct {
@@ -157,8 +154,6 @@ type DefaultAccountManager struct {
// userDeleteFromIDPEnabled allows to delete user from IDP when user is deleted from account
userDeleteFromIDPEnabled bool
integratedPeerValidator integrated_approval.IntegratedApproval
}
// Settings represents Account settings structure that can be modified via API and Dashboard
@@ -210,7 +205,6 @@ type Account struct {
// User.Id it was created by
CreatedBy string
CreatedAt time.Time
Domain string `gorm:"index"`
DomainCategory string
IsDomainPrimaryAccount bool
@@ -393,14 +387,12 @@ func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap {
Network: a.Network.Copy(),
}
}
validatedPeers := additions.ValidatePeers([]*nbpeer.Peer{peer})
if len(validatedPeers) == 0 {
return &NetworkMap{
Network: a.Network.Copy(),
}
}
aclPeers, firewallRules := a.getPeerConnectionResources(peerID)
// exclude expired peers
var peersToConnect []*nbpeer.Peer
@@ -579,20 +571,6 @@ func (a *Account) FindSetupKey(setupKey string) (*SetupKey, error) {
return key, nil
}
// GetPeerGroupsList return with the list of groups ID.
func (a *Account) GetPeerGroupsList(peerID string) []string {
var grps []string
for groupID, group := range a.Groups {
for _, id := range group.Peers {
if id == peerID {
grps = append(grps, groupID)
break
}
}
}
return grps
}
func (a *Account) getUserGroups(userID string) ([]string, error) {
user, err := a.FindUser(userID)
if err != nil {
@@ -705,7 +683,6 @@ func (a *Account) Copy() *Account {
return &Account{
Id: a.Id,
CreatedBy: a.CreatedBy,
CreatedAt: a.CreatedAt,
Domain: a.Domain,
DomainCategory: a.DomainCategory,
IsDomainPrimaryAccount: a.IsDomainPrimaryAccount,
@@ -847,7 +824,6 @@ func (a *Account) UserGroupsRemoveFromPeers(userID string, groups ...string) {
func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManager idp.Manager,
singleAccountModeDomain string, dnsDomain string, eventStore activity.Store, geo *geolocation.Geolocation,
userDeleteFromIDPEnabled bool,
integratedPeerValidator integrated_approval.IntegratedApproval,
) (*DefaultAccountManager, error) {
am := &DefaultAccountManager{
Store: store,
@@ -861,7 +837,6 @@ func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManage
eventStore: eventStore,
peerLoginExpiry: NewDefaultScheduler(),
userDeleteFromIDPEnabled: userDeleteFromIDPEnabled,
integratedPeerValidator: integratedPeerValidator,
}
allAccounts := store.GetAllAccounts()
// enable single account mode only if configured by user and number of existing accounts is not grater than 1
@@ -925,10 +900,6 @@ func (am *DefaultAccountManager) GetExternalCacheManager() ExternalCacheManager
return am.externalCacheManager
}
func (am *DefaultAccountManager) GetIdpManager() idp.Manager {
return am.idpManager
}
// UpdateAccountSettings updates Account settings.
// Only users with role UserRoleAdmin can update the account.
// User that performs the update has to belong to the account.
@@ -946,7 +917,12 @@ func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string,
unlock := am.Store.AcquireAccountLock(accountID)
defer unlock()
account, err := am.Store.GetAccount(accountID)
account, err := am.Store.GetAccountByUser(userID)
if err != nil {
return nil, err
}
err = additions.ValidateExtraSettings(newSettings.Extra, account.Settings.Extra, account.Peers, userID, accountID, am.eventStore)
if err != nil {
return nil, err
}
@@ -960,11 +936,6 @@ func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string,
return nil, status.Errorf(status.PermissionDenied, "user is not allowed to update account")
}
err = additions.ValidateExtraSettings(newSettings.Extra, account.Settings.Extra, account.Peers, userID, accountID, am.eventStore)
if err != nil {
return nil, err
}
oldSettings := account.Settings
if oldSettings.PeerLoginExpirationEnabled != newSettings.PeerLoginExpirationEnabled {
event := activity.AccountPeerLoginExpirationEnabled
@@ -1899,7 +1870,6 @@ func newAccountWithId(accountID, userID, domain string) *Account {
acc := &Account{
Id: accountID,
CreatedAt: time.Now().UTC(),
SetupKeys: setupKeys,
Network: network,
Peers: peers,

View File

@@ -3,17 +3,11 @@ package account
type ExtraSettings struct {
// PeerApprovalEnabled enables or disables the need for peers bo be approved by an administrator
PeerApprovalEnabled bool
// IntegratedApprovalGroups list of group IDs to be used with integrated approval configurations
IntegratedApprovalGroups []string `gorm:"serializer:json"`
}
// Copy copies the ExtraSettings struct
func (e *ExtraSettings) Copy() *ExtraSettings {
var cpGroup []string
return &ExtraSettings{
PeerApprovalEnabled: e.PeerApprovalEnabled,
IntegratedApprovalGroups: append(cpGroup, e.IntegratedApprovalGroups...),
PeerApprovalEnabled: e.PeerApprovalEnabled,
}
}

View File

@@ -12,34 +12,20 @@ import (
"time"
"github.com/golang-jwt/jwt"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/activity"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
"github.com/netbirdio/netbird/route"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/jwtclaims"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
"github.com/netbirdio/netbird/route"
)
type MocIntegratedApproval struct {
}
func (MocIntegratedApproval) PreparePeer(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) *nbpeer.Peer {
return peer
}
func (MocIntegratedApproval) IsRequiresApproval(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) bool {
return false
}
func (MocIntegratedApproval) Stop() {
}
func verifyCanAddPeerToAccount(t *testing.T, manager AccountManager, account *Account, userID string) {
t.Helper()
peer := &nbpeer.Peer{
@@ -108,10 +94,6 @@ func verifyNewAccountHasDefaultFields(t *testing.T, account *Account, createdBy
t.Errorf("expecting newly created account to be created by user %s, got %s", createdBy, account.CreatedBy)
}
if account.CreatedAt.IsZero() {
t.Errorf("expecting newly created account to have a non-zero creation time")
}
if account.Domain != domain {
t.Errorf("expecting newly created account to have domain %s, got %s", domain, account.Domain)
}
@@ -1491,7 +1473,6 @@ func TestAccount_Copy(t *testing.T) {
account := &Account{
Id: "account1",
CreatedBy: "tester",
CreatedAt: time.Now().UTC(),
Domain: "test.com",
DomainCategory: "public",
IsDomainPrimaryAccount: true,
@@ -2237,7 +2218,7 @@ func createManager(t *testing.T) (*DefaultAccountManager, error) {
return nil, err
}
eventStore := &activity.InMemoryEventStore{}
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedApproval{})
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false)
}
func createStore(t *testing.T) (Store, error) {

View File

@@ -9,7 +9,7 @@ const (
)
// ActivityDescriber is an interface that describes an activity
type ActivityDescriber interface { //nolint:revive
type ActivityDescriber interface {
StringCode() string
Message() string
}

View File

@@ -193,7 +193,7 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
return nil, err
}
eventStore := &activity.InMemoryEventStore{}
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedApproval{})
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false)
}
func createDNSStore(t *testing.T) (Store, error) {

View File

@@ -3,7 +3,6 @@ package geolocation
import (
"encoding/csv"
"fmt"
"io"
"net/url"
"os"
"path"
@@ -36,7 +35,7 @@ func loadGeolocationDatabases(dataDir string) error {
if err := decompressTarGzFile(src, dst); err != nil {
return err
}
return copyFile(path.Join(dst, MMDBFileName), path.Join(dataDir, MMDBFileName))
return os.Rename(path.Join(dst, MMDBFileName), path.Join(dataDir, MMDBFileName))
}
if err := loadDatabase(
geoLiteCitySha256TarURL,
@@ -186,25 +185,3 @@ func getDatabaseFileName(urlStr string) string {
fileName := fmt.Sprintf("%s.%s", path.Base(u.Path), ext)
return fileName
}
// copyFile performs a file copy operation from the source file to the destination.
func copyFile(src string, dst string) error {
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
return nil
}

View File

@@ -274,15 +274,6 @@ func (am *DefaultAccountManager) DeleteGroup(accountId, userId, groupID string)
}
}
// check integrated peer approval
if account.Settings.Extra != nil {
for _, integratedPeerApprovalGroups := range account.Settings.Extra.IntegratedApprovalGroups {
if groupID == integratedPeerApprovalGroups {
return &GroupLinkError{"integrated approval", g.Name}
}
}
}
delete(account.Groups, groupID)
account.Network.IncSerial()

View File

@@ -288,10 +288,6 @@ func extractPeerMeta(loginReq *proto.LoginRequest) nbpeer.PeerSystemMeta {
SystemSerialNumber: loginReq.GetMeta().GetSysSerialNumber(),
SystemProductName: loginReq.GetMeta().GetSysProductName(),
SystemManufacturer: loginReq.GetMeta().GetSysManufacturer(),
Environment: nbpeer.Environment{
Cloud: loginReq.GetMeta().GetEnvironment().GetCloud(),
Platform: loginReq.GetMeta().GetEnvironment().GetPlatform(),
},
}
}

View File

@@ -121,7 +121,7 @@ components:
description: Last time this user performed a login to the dashboard
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
example: 2023-05-05T09:00:35.477782Z
auto_groups:
description: Group IDs to auto-assign to peers registered by this user
type: array
@@ -259,7 +259,7 @@ components:
description: Last time peer connected to Netbird's management service
type: string
format: date-time
example: "2023-05-05T10:05:26.420578Z"
example: 2023-05-05T10:05:26.420578Z
os:
description: Peer's operating system and version
type: string
@@ -313,7 +313,7 @@ components:
description: Last time this peer performed log in (authentication). E.g., user authenticated.
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
example: 2023-05-05T09:00:35.477782Z
approval_required:
description: (Cloud only) Indicates whether peer needs approval
type: boolean
@@ -405,7 +405,7 @@ components:
description: Setup Key expiration date
type: string
format: date-time
example: "2023-06-01T14:47:22.291057Z"
example: 2023-06-01T14:47:22.291057Z
type:
description: Setup key type, one-off for single time usage and reusable
type: string
@@ -426,7 +426,7 @@ components:
description: Setup key last usage date
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
example: 2023-05-05T09:00:35.477782Z
state:
description: Setup key status, "valid", "overused","expired" or "revoked"
type: string
@@ -441,7 +441,7 @@ components:
description: Setup key last update date
type: string
format: date-time
example: "2023-05-05T09:00:35.477782Z"
example: 2023-05-05T09:00:35.477782Z
usage_limit:
description: A number of times this key can be used. The value of 0 indicates the unlimited usage.
type: integer
@@ -522,7 +522,7 @@ components:
description: Date the token expires
type: string
format: date-time
example: "2023-05-05T14:38:28.977616Z"
example: 2023-05-05T14:38:28.977616Z
created_by:
description: User ID of the user who created the token
type: string
@@ -531,12 +531,12 @@ components:
description: Date the token was created
type: string
format: date-time
example: "2023-05-02T14:48:20.465209Z"
example: 2023-05-02T14:48:20.465209Z
last_used:
description: Date the token was last used
type: string
format: date-time
example: "2023-05-04T12:45:25.9723616Z"
example: 2023-05-04T12:45:25.9723616Z
required:
- id
- name
@@ -862,8 +862,8 @@ components:
$ref: '#/components/schemas/OSVersionCheck'
geo_location_check:
$ref: '#/components/schemas/GeoLocationCheck'
peer_network_range_check:
$ref: '#/components/schemas/PeerNetworkRangeCheck'
private_network_check:
$ref: '#/components/schemas/PrivateNetworkCheck'
NBVersionCheck:
description: Posture check for the version of NetBird
type: object
@@ -934,16 +934,16 @@ components:
required:
- locations
- action
PeerNetworkRangeCheck:
description: Posture check for allow or deny access based on peer local network addresses
PrivateNetworkCheck:
description: Posture check for allow or deny private network
type: object
properties:
ranges:
description: List of peer network ranges in CIDR notation
description: List of private network ranges in CIDR notation
type: array
items:
type: string
example: ["192.168.1.0/24", "10.0.0.0/8", "2001:db8:1234:1a00::/56"]
example: ["192.168.1.0/24", "10.0.0.0/8"]
action:
description: Action to take upon policy match
type: string
@@ -979,7 +979,7 @@ components:
type: string
example: "Germany"
country_code:
$ref: '#/components/schemas/CountryCode'
$ref: '#/components/schemas/CountryCode'
required:
- country_name
- country_code
@@ -1197,7 +1197,7 @@ components:
description: The date and time when the event occurred
type: string
format: date-time
example: "2023-05-05T10:04:37.473542Z"
example: 2023-05-05T10:04:37.473542Z
activity:
description: The activity that occurred during the event
type: string

View File

@@ -74,12 +74,6 @@ const (
NameserverNsTypeUdp NameserverNsType = "udp"
)
// Defines values for PeerNetworkRangeCheckAction.
const (
PeerNetworkRangeCheckActionAllow PeerNetworkRangeCheckAction = "allow"
PeerNetworkRangeCheckActionDeny PeerNetworkRangeCheckAction = "deny"
)
// Defines values for PolicyRuleAction.
const (
PolicyRuleActionAccept PolicyRuleAction = "accept"
@@ -122,6 +116,12 @@ const (
PolicyRuleUpdateProtocolUdp PolicyRuleUpdateProtocol = "udp"
)
// Defines values for PrivateNetworkCheckAction.
const (
PrivateNetworkCheckActionAllow PrivateNetworkCheckAction = "allow"
PrivateNetworkCheckActionDeny PrivateNetworkCheckAction = "deny"
)
// Defines values for UserStatus.
const (
UserStatusActive UserStatus = "active"
@@ -199,8 +199,8 @@ type Checks struct {
// OsVersionCheck Posture check for the version of operating system
OsVersionCheck *OSVersionCheck `json:"os_version_check,omitempty"`
// PeerNetworkRangeCheck Posture check for allow or deny access based on peer local network addresses
PeerNetworkRangeCheck *PeerNetworkRangeCheck `json:"peer_network_range_check,omitempty"`
// PrivateNetworkCheck Posture check for allow or deny private network
PrivateNetworkCheck *PrivateNetworkCheck `json:"private_network_check,omitempty"`
}
// City Describe city geographical location information
@@ -656,18 +656,6 @@ type PeerMinimum struct {
Name string `json:"name"`
}
// PeerNetworkRangeCheck Posture check for allow or deny access based on peer local network addresses
type PeerNetworkRangeCheck struct {
// Action Action to take upon policy match
Action PeerNetworkRangeCheckAction `json:"action"`
// Ranges List of peer network ranges in CIDR notation
Ranges []string `json:"ranges"`
}
// PeerNetworkRangeCheckAction Action to take upon policy match
type PeerNetworkRangeCheckAction string
// PeerRequest defines model for PeerRequest.
type PeerRequest struct {
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
@@ -910,6 +898,18 @@ type PostureCheckUpdate struct {
Name string `json:"name"`
}
// PrivateNetworkCheck Posture check for allow or deny private network
type PrivateNetworkCheck struct {
// Action Action to take upon policy match
Action PrivateNetworkCheckAction `json:"action"`
// Ranges List of private network ranges in CIDR notation
Ranges []string `json:"ranges"`
}
// PrivateNetworkCheckAction Action to take upon policy match
type PrivateNetworkCheckAction string
// Route defines model for Route.
type Route struct {
// Description Route description

View File

@@ -9,6 +9,7 @@ import (
"github.com/rs/cors"
"github.com/netbirdio/management-integrations/integrations"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/http/middleware"

View File

@@ -213,8 +213,8 @@ func (p *PostureChecksHandler) savePostureChecks(
postureChecks.Checks.GeoLocationCheck = toPostureGeoLocationCheck(geoLocationCheck)
}
if peerNetworkRangeCheck := req.Checks.PeerNetworkRangeCheck; peerNetworkRangeCheck != nil {
postureChecks.Checks.PeerNetworkRangeCheck, err = toPeerNetworkRangeCheck(peerNetworkRangeCheck)
if privateNetworkCheck := req.Checks.PrivateNetworkCheck; privateNetworkCheck != nil {
postureChecks.Checks.PrivateNetworkCheck, err = toPrivateNetworkCheck(privateNetworkCheck)
if err != nil {
util.WriteError(status.Errorf(status.InvalidArgument, "invalid network prefix"), w)
return
@@ -235,7 +235,7 @@ func validatePostureChecksUpdate(req api.PostureCheckUpdate) error {
}
if req.Checks == nil || (req.Checks.NbVersionCheck == nil && req.Checks.OsVersionCheck == nil &&
req.Checks.GeoLocationCheck == nil && req.Checks.PeerNetworkRangeCheck == nil) {
req.Checks.GeoLocationCheck == nil && req.Checks.PrivateNetworkCheck == nil) {
return status.Errorf(status.InvalidArgument, "posture checks shouldn't be empty")
}
@@ -278,17 +278,17 @@ func validatePostureChecksUpdate(req api.PostureCheckUpdate) error {
}
}
if peerNetworkRangeCheck := req.Checks.PeerNetworkRangeCheck; peerNetworkRangeCheck != nil {
if peerNetworkRangeCheck.Action == "" {
return status.Errorf(status.InvalidArgument, "action for peer network range check shouldn't be empty")
if privateNetworkCheck := req.Checks.PrivateNetworkCheck; privateNetworkCheck != nil {
if privateNetworkCheck.Action == "" {
return status.Errorf(status.InvalidArgument, "action for private network check shouldn't be empty")
}
allowedActions := []api.PeerNetworkRangeCheckAction{api.PeerNetworkRangeCheckActionAllow, api.PeerNetworkRangeCheckActionDeny}
if !slices.Contains(allowedActions, peerNetworkRangeCheck.Action) {
return status.Errorf(status.InvalidArgument, "action for peer network range check is not valid value")
allowedActions := []api.PrivateNetworkCheckAction{api.PrivateNetworkCheckActionAllow, api.PrivateNetworkCheckActionDeny}
if !slices.Contains(allowedActions, privateNetworkCheck.Action) {
return status.Errorf(status.InvalidArgument, "action for private network check is not valid value")
}
if len(peerNetworkRangeCheck.Ranges) == 0 {
return status.Errorf(status.InvalidArgument, "network ranges for peer network range check shouldn't be empty")
if len(privateNetworkCheck.Ranges) == 0 {
return status.Errorf(status.InvalidArgument, "network ranges for private network check shouldn't be empty")
}
}
@@ -318,8 +318,8 @@ func toPostureChecksResponse(postureChecks *posture.Checks) *api.PostureCheck {
checks.GeoLocationCheck = toGeoLocationCheckResponse(postureChecks.Checks.GeoLocationCheck)
}
if postureChecks.Checks.PeerNetworkRangeCheck != nil {
checks.PeerNetworkRangeCheck = toPeerNetworkRangeCheckResponse(postureChecks.Checks.PeerNetworkRangeCheck)
if postureChecks.Checks.PrivateNetworkCheck != nil {
checks.PrivateNetworkCheck = toPrivateNetworkCheckResponse(postureChecks.Checks.PrivateNetworkCheck)
}
return &api.PostureCheck{
@@ -369,19 +369,19 @@ func toPostureGeoLocationCheck(apiGeoLocationCheck *api.GeoLocationCheck) *postu
}
}
func toPeerNetworkRangeCheckResponse(check *posture.PeerNetworkRangeCheck) *api.PeerNetworkRangeCheck {
func toPrivateNetworkCheckResponse(check *posture.PrivateNetworkCheck) *api.PrivateNetworkCheck {
netPrefixes := make([]string, 0, len(check.Ranges))
for _, netPrefix := range check.Ranges {
netPrefixes = append(netPrefixes, netPrefix.String())
}
return &api.PeerNetworkRangeCheck{
return &api.PrivateNetworkCheck{
Ranges: netPrefixes,
Action: api.PeerNetworkRangeCheckAction(check.Action),
Action: api.PrivateNetworkCheckAction(check.Action),
}
}
func toPeerNetworkRangeCheck(check *api.PeerNetworkRangeCheck) (*posture.PeerNetworkRangeCheck, error) {
func toPrivateNetworkCheck(check *api.PrivateNetworkCheck) (*posture.PrivateNetworkCheck, error) {
prefixes := make([]netip.Prefix, 0)
for _, prefix := range check.Ranges {
parsedPrefix, err := netip.ParsePrefix(prefix)
@@ -391,7 +391,7 @@ func toPeerNetworkRangeCheck(check *api.PeerNetworkRangeCheck) (*posture.PeerNet
prefixes = append(prefixes, parsedPrefix)
}
return &posture.PeerNetworkRangeCheck{
return &posture.PrivateNetworkCheck{
Ranges: prefixes,
Action: string(check.Action),
}, nil

View File

@@ -131,7 +131,7 @@ func TestGetPostureCheck(t *testing.T) {
ID: "privateNetworkPostureCheck",
Name: "privateNetwork",
Checks: posture.ChecksDefinition{
PeerNetworkRangeCheck: &posture.PeerNetworkRangeCheck{
PrivateNetworkCheck: &posture.PrivateNetworkCheck{
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/24"),
},
@@ -375,7 +375,7 @@ func TestPostureCheckUpdate(t *testing.T) {
},
},
{
name: "Create Posture Checks Peer Network Range",
name: "Create Posture Checks Private Network",
requestType: http.MethodPost,
requestPath: "/api/posture-checks",
requestBody: bytes.NewBuffer(
@@ -383,7 +383,7 @@ func TestPostureCheckUpdate(t *testing.T) {
"name": "default",
"description": "default",
"checks": {
"peer_network_range_check": {
"private_network_check": {
"action": "allow",
"ranges": [
"10.0.0.0/8"
@@ -398,11 +398,11 @@ func TestPostureCheckUpdate(t *testing.T) {
Name: "default",
Description: str("default"),
Checks: api.Checks{
PeerNetworkRangeCheck: &api.PeerNetworkRangeCheck{
PrivateNetworkCheck: &api.PrivateNetworkCheck{
Ranges: []string{
"10.0.0.0/8",
},
Action: api.PeerNetworkRangeCheckActionAllow,
Action: api.PrivateNetworkCheckActionAllow,
},
},
},
@@ -715,14 +715,14 @@ func TestPostureCheckUpdate(t *testing.T) {
expectedBody: false,
},
{
name: "Update Posture Checks Peer Network Range",
name: "Update Posture Checks Private Network",
requestType: http.MethodPut,
requestPath: "/api/posture-checks/peerNetworkRangePostureCheck",
requestPath: "/api/posture-checks/privateNetworkPostureCheck",
requestBody: bytes.NewBuffer(
[]byte(`{
"name": "default",
"checks": {
"peer_network_range_check": {
"private_network_check": {
"action": "deny",
"ranges": [
"192.168.1.0/24"
@@ -737,11 +737,11 @@ func TestPostureCheckUpdate(t *testing.T) {
Name: "default",
Description: str(""),
Checks: api.Checks{
PeerNetworkRangeCheck: &api.PeerNetworkRangeCheck{
PrivateNetworkCheck: &api.PrivateNetworkCheck{
Ranges: []string{
"192.168.1.0/24",
},
Action: api.PeerNetworkRangeCheckActionDeny,
Action: api.PrivateNetworkCheckActionDeny,
},
},
},
@@ -784,10 +784,10 @@ func TestPostureCheckUpdate(t *testing.T) {
},
},
&posture.Checks{
ID: "peerNetworkRangePostureCheck",
Name: "peerNetworkRange",
ID: "privateNetworkPostureCheck",
Name: "privateNetwork",
Checks: posture.ChecksDefinition{
PeerNetworkRangeCheck: &posture.PeerNetworkRangeCheck{
PrivateNetworkCheck: &posture.PrivateNetworkCheck{
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/24"),
},
@@ -891,50 +891,29 @@ func TestPostureCheck_validatePostureChecksUpdate(t *testing.T) {
err = validatePostureChecksUpdate(api.PostureCheckUpdate{Name: "Default", Checks: &api.Checks{OsVersionCheck: &osVersionCheck}})
assert.NoError(t, err)
// valid peer network range check
peerNetworkRangeCheck := api.PeerNetworkRangeCheck{
Action: api.PeerNetworkRangeCheckActionAllow,
// valid private network check
privateNetworkCheck := api.PrivateNetworkCheck{
Action: api.PrivateNetworkCheckActionAllow,
Ranges: []string{
"192.168.1.0/24", "10.0.0.0/8",
},
}
err = validatePostureChecksUpdate(
api.PostureCheckUpdate{
Name: "Default",
Checks: &api.Checks{
PeerNetworkRangeCheck: &peerNetworkRangeCheck,
},
},
)
err = validatePostureChecksUpdate(api.PostureCheckUpdate{Name: "Default", Checks: &api.Checks{PrivateNetworkCheck: &privateNetworkCheck}})
assert.NoError(t, err)
// invalid peer network range check
peerNetworkRangeCheck = api.PeerNetworkRangeCheck{
Action: api.PeerNetworkRangeCheckActionDeny,
// invalid private network check
privateNetworkCheck = api.PrivateNetworkCheck{
Action: api.PrivateNetworkCheckActionDeny,
Ranges: []string{},
}
err = validatePostureChecksUpdate(
api.PostureCheckUpdate{
Name: "Default",
Checks: &api.Checks{
PeerNetworkRangeCheck: &peerNetworkRangeCheck,
},
},
)
err = validatePostureChecksUpdate(api.PostureCheckUpdate{Name: "Default", Checks: &api.Checks{PrivateNetworkCheck: &privateNetworkCheck}})
assert.Error(t, err)
// invalid peer network range check
peerNetworkRangeCheck = api.PeerNetworkRangeCheck{
// invalid private network check
privateNetworkCheck = api.PrivateNetworkCheck{
Action: "unknownAction",
Ranges: []string{},
}
err = validatePostureChecksUpdate(
api.PostureCheckUpdate{
Name: "Default",
Checks: &api.Checks{
PeerNetworkRangeCheck: &peerNetworkRangeCheck,
},
},
)
err = validatePostureChecksUpdate(api.PostureCheckUpdate{Name: "Default", Checks: &api.Checks{PrivateNetworkCheck: &privateNetworkCheck}})
assert.Error(t, err)
}

View File

@@ -114,22 +114,6 @@ type auth0Profile struct {
LastLogin string `json:"last_login"`
}
// Connections represents a single Auth0 connection
// https://auth0.com/docs/api/management/v2/connections/get-connections
type Connection struct {
Id string `json:"id"`
Name string `json:"name"`
DisplayName string `json:"display_name"`
IsDomainConnection bool `json:"is_domain_connection"`
Realms []string `json:"realms"`
Metadata map[string]string `json:"metadata"`
Options ConnectionOptions `json:"options"`
}
type ConnectionOptions struct {
DomainAliases []string `json:"domain_aliases"`
}
// NewAuth0Manager creates a new instance of the Auth0Manager
func NewAuth0Manager(config Auth0ClientConfig, appMetrics telemetry.AppMetrics) (*Auth0Manager, error) {
httpTransport := http.DefaultTransport.(*http.Transport).Clone()
@@ -597,13 +581,13 @@ func (am *Auth0Manager) GetAllAccounts() (map[string][]*UserData, error) {
body, err := io.ReadAll(jobResp.Body)
if err != nil {
log.Debugf("Couldn't read export job response; %v", err)
log.Debugf("Coudln't read export job response; %v", err)
return nil, err
}
err = am.helper.Unmarshal(body, &exportJobResp)
if err != nil {
log.Debugf("Couldn't unmarshal export job response; %v", err)
log.Debugf("Coudln't unmarshal export job response; %v", err)
return nil, err
}
@@ -651,7 +635,7 @@ func (am *Auth0Manager) GetUserByEmail(email string) ([]*UserData, error) {
err = am.helper.Unmarshal(body, &userResp)
if err != nil {
log.Debugf("Couldn't unmarshal export job response; %v", err)
log.Debugf("Coudln't unmarshal export job response; %v", err)
return nil, err
}
@@ -700,13 +684,13 @@ func (am *Auth0Manager) CreateUser(email, name, accountID, invitedByEmail string
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Debugf("Couldn't read export job response; %v", err)
log.Debugf("Coudln't read export job response; %v", err)
return nil, err
}
err = am.helper.Unmarshal(body, &createResp)
if err != nil {
log.Debugf("Couldn't unmarshal export job response; %v", err)
log.Debugf("Coudln't unmarshal export job response; %v", err)
return nil, err
}
@@ -793,56 +777,6 @@ func (am *Auth0Manager) DeleteUser(userID string) error {
return nil
}
// GetAllConnections returns detailed list of all connections filtered by given params.
// Note this method is not part of the IDP Manager interface as this is Auth0 specific.
func (am *Auth0Manager) GetAllConnections(strategy []string) ([]Connection, error) {
var connections []Connection
q := make(url.Values)
q.Set("strategy", strings.Join(strategy, ","))
req, err := am.createRequest(http.MethodGet, "/api/v2/connections?"+q.Encode(), nil)
if err != nil {
return connections, err
}
resp, err := am.httpClient.Do(req)
if err != nil {
log.Debugf("execute get connections request: %v", err)
if am.appMetrics != nil {
am.appMetrics.IDPMetrics().CountRequestError()
}
return connections, err
}
defer func() {
err = resp.Body.Close()
if err != nil {
log.Errorf("close get connections request body: %v", err)
}
}()
if resp.StatusCode != 200 {
if am.appMetrics != nil {
am.appMetrics.IDPMetrics().CountRequestStatusError()
}
return connections, fmt.Errorf("unable to get connections, statusCode %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Debugf("Couldn't read get connections response; %v", err)
return connections, err
}
err = am.helper.Unmarshal(body, &connections)
if err != nil {
log.Debugf("Couldn't unmarshal get connection response; %v", err)
return connections, err
}
return connections, err
}
// checkExportJobStatus checks the status of the job created at CreateExportUsersJob.
// If the status is "completed", then return the downloadLink
func (am *Auth0Manager) checkExportJobStatus(jobID string) (bool, string, error) {

View File

@@ -1,132 +0,0 @@
package server
import (
"errors"
"github.com/google/martian/v3/log"
"github.com/netbirdio/netbird/management/server/account"
)
// UpdateIntegratedApprovalGroups updates the integrated approval groups for a specified account.
// It retrieves the account associated with the provided userID, then updates the integrated approval groups
// with the provided list of group ids. The updated account is then saved.
//
// Parameters:
// - accountID: The ID of the account for which integrated approval groups are to be updated.
// - userID: The ID of the user whose account is being updated.
// - groups: A slice of strings representing the ids of integrated approval groups to be updated.
//
// Returns:
// - error: An error if any occurred during the process, otherwise returns nil
func (am *DefaultAccountManager) UpdateIntegratedApprovalGroups(accountID string, userID string, groups []string) error {
unlock := am.Store.AcquireAccountLock(accountID)
defer unlock()
ok, err := am.GroupValidation(accountID, groups)
if err != nil {
log.Debugf("error validating groups: %s", err.Error())
return err
}
if !ok {
log.Debugf("invalid groups")
return errors.New("invalid groups")
}
a, err := am.Store.GetAccountByUser(userID)
if err != nil {
return err
}
var extra *account.ExtraSettings
if a.Settings.Extra != nil {
extra = a.Settings.Extra
} else {
extra = &account.ExtraSettings{}
a.Settings.Extra = extra
}
extra.IntegratedApprovalGroups = groups
am.cleanIntegratedApprovalFlag(a, groups)
err = am.updateFlags(a, groups)
if err != nil {
saveErr := am.Store.SaveAccount(a)
if saveErr != nil {
log.Errorf("failed to save account: %s", saveErr)
}
return err
}
return am.Store.SaveAccount(a)
}
func (am *DefaultAccountManager) GroupValidation(accountId string, groups []string) (bool, error) {
if len(groups) == 0 {
return true, nil
}
accountsGroups, err := am.ListGroups(accountId)
if err != nil {
return false, err
}
for _, group := range groups {
var found bool
for _, accountGroup := range accountsGroups {
if accountGroup.ID == group {
found = true
break
}
}
if !found {
return false, nil
}
}
return true, nil
}
// updateFlags set the requiresIntegratedApproval flag to true for all peers in the account what is part of the groups, but the peer not part of the already approved list in the edr db
func (am *DefaultAccountManager) updateFlags(a *Account, groups []string) error {
approvedPeers, err := am.integratedPeerValidator.ApprovedPeersList(a.Id)
if err != nil {
log.Errorf("failed to get approved peers list: %s", err)
return err
}
for peerID, peer := range a.Peers {
peerGroups := a.GetPeerGroupsList(peerID)
if !isPeerAssignedToIntegratedApprovalGroup(peerGroups, groups) {
continue
}
// set true only that case if not yet approved in the edr db
_, ok := approvedPeers[peerID]
if ok {
continue
}
peer.Status.RequiresIntegratedApproval = true
}
return nil
}
// cleanIntegratedApprovalFlag set the requireIntegratedApproval flag to false for all peers in the account what is not part of the groups
func (am *DefaultAccountManager) cleanIntegratedApprovalFlag(a *Account, groups []string) {
for peerID, peer := range a.Peers {
peerGroups := a.GetPeerGroupsList(peerID)
if isPeerAssignedToIntegratedApprovalGroup(peerGroups, groups) {
continue
}
peer.Status.RequiresIntegratedApproval = false
}
}
func isPeerAssignedToIntegratedApprovalGroup(peersGroup []string, integratedApprovalGroups []string) bool {
for _, peerGroup := range peersGroup {
for _, ig := range integratedApprovalGroups {
if ig == peerGroup {
return true
}
}
}
return false
}

View File

@@ -1,14 +0,0 @@
package integrated_approval
import (
"github.com/netbirdio/netbird/management/server/account"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
)
// IntegratedApproval interface exists to avoid the circle dependencies
type IntegratedApproval interface {
PreparePeer(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) *nbpeer.Peer
IsRequiresApproval(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) (bool, bool)
ApprovedPeersList(id string) (map[string]struct{}, error)
Stop()
}

View File

@@ -9,6 +9,8 @@ import (
"testing"
"time"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc"
@@ -17,7 +19,6 @@ import (
"github.com/netbirdio/netbird/encryption"
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/util"
)
@@ -412,7 +413,7 @@ func startManagement(t *testing.T, config *Config) (*grpc.Server, string, error)
peersUpdateManager := NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
accountManager, err := BuildManager(store, peersUpdateManager, nil, "", "",
eventStore, nil, false, MocIntegratedApproval{})
eventStore, nil, false)
if err != nil {
return nil, "", err
}

View File

@@ -2,8 +2,6 @@ package server_test
import (
"context"
"github.com/netbirdio/netbird/management/server/account"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"math/rand"
"net"
"os"
@@ -12,19 +10,24 @@ import (
sync2 "sync"
"time"
pb "github.com/golang/protobuf/proto" //nolint
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc"
"github.com/netbirdio/netbird/management/server/activity"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/keepalive"
"github.com/netbirdio/netbird/management/server"
pb "github.com/golang/protobuf/proto" //nolint
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/encryption"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/util"
)
@@ -445,21 +448,6 @@ var _ = Describe("Management service", func() {
})
})
type MocIntegratedApproval struct {
}
func (MocIntegratedApproval) PreparePeer(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) *nbpeer.Peer {
return peer
}
func (MocIntegratedApproval) IsRequiresApproval(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) bool {
return false
}
func (MocIntegratedApproval) Stop() {
}
func loginPeerWithValidSetupKey(serverPubKey wgtypes.Key, key wgtypes.Key, client mgmtProto.ManagementServiceClient) *mgmtProto.LoginResponse {
defer GinkgoRecover()
@@ -516,7 +504,7 @@ func startServer(config *server.Config) (*grpc.Server, net.Listener) {
peersUpdateManager := server.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "", "",
eventStore, nil, false, MocIntegratedApproval{})
eventStore, nil, false)
if err != nil {
log.Fatalf("failed creating a manager: %v", err)
}

View File

@@ -11,7 +11,6 @@ import (
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/jwtclaims"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
@@ -94,9 +93,6 @@ type MockAccountManager struct {
DeletePostureChecksFunc func(accountID, postureChecksID, userID string) error
ListPostureChecksFunc func(accountID, userID string) ([]*posture.Checks, error)
GetUsageFunc func(ctx context.Context, accountID string, start, end time.Time) (*server.AccountUsageStats, error)
GetIdpManagerFunc func() idp.Manager
UpdateIntegratedApprovalGroupsFunc func(accountID string, userID string, groups []string) error
GroupValidationFunc func(accountId string, groups []string) (bool, error)
}
// GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface
@@ -709,34 +705,10 @@ func (am *MockAccountManager) ListPostureChecks(accountID, userID string) ([]*po
return nil, status.Errorf(codes.Unimplemented, "method ListPostureChecks is not implemented")
}
// GetUsage mocks GetUsage of the AccountManager interface
// GetUsage mocks GetCurrentUsage of the AccountManager interface
func (am *MockAccountManager) GetUsage(ctx context.Context, accountID string, start time.Time, end time.Time) (*server.AccountUsageStats, error) {
if am.GetUsageFunc != nil {
return am.GetUsageFunc(ctx, accountID, start, end)
}
return nil, status.Errorf(codes.Unimplemented, "method GetUsage is not implemented")
}
// GetIdpManager mocks GetIdpManager of the AccountManager interface
func (am *MockAccountManager) GetIdpManager() idp.Manager {
if am.GetIdpManagerFunc != nil {
return am.GetIdpManagerFunc()
}
return nil
}
// UpdateIntegratedApprovalGroups mocks UpdateIntegratedApprovalGroups of the AccountManager interface
func (am *MockAccountManager) UpdateIntegratedApprovalGroups(accountID string, userID string, groups []string) error {
if am.UpdateIntegratedApprovalGroupsFunc != nil {
return am.UpdateIntegratedApprovalGroupsFunc(accountID, userID, groups)
}
return status.Errorf(codes.Unimplemented, "method UpdateIntegratedApprovalGroups is not implemented")
}
// GroupValidation mocks GroupValidation of the AccountManager interface
func (am *MockAccountManager) GroupValidation(accountId string, groups []string) (bool, error) {
if am.GroupValidationFunc != nil {
return am.GroupValidationFunc(accountId, groups)
}
return false, status.Errorf(codes.Unimplemented, "method GroupValidation is not implemented")
}

View File

@@ -759,7 +759,7 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
return nil, err
}
eventStore := &activity.InMemoryEventStore{}
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "", eventStore, nil, false, MocIntegratedApproval{})
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "", eventStore, nil, false)
}
func createNSStore(t *testing.T) (Store, error) {

View File

@@ -7,13 +7,16 @@ import (
"time"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/management-integrations/additions"
"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/activity"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/status"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/proto"
)
// PeerSync used as a data object between the gRPC API and AccountManager on Sync request.
@@ -296,7 +299,6 @@ func (am *DefaultAccountManager) GetNetworkMap(peerID string) (*NetworkMap, erro
if peer == nil {
return nil, status.Errorf(status.NotFound, "peer with ID %s not found", peerID)
}
return account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
}
@@ -408,8 +410,6 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *nbpeer.P
return nil, nil, err
}
registrationTime := time.Now().UTC()
newPeer := &nbpeer.Peer{
ID: xid.New().String(),
Key: peer.Key,
@@ -419,15 +419,18 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *nbpeer.P
Name: peer.Meta.Hostname,
DNSLabel: newLabel,
UserID: userID,
Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime},
Status: &nbpeer.PeerStatus{Connected: false, LastSeen: time.Now().UTC()},
SSHEnabled: false,
SSHKey: peer.SSHKey,
LastLogin: registrationTime,
CreatedAt: registrationTime,
LastLogin: time.Now().UTC(),
LoginExpirationEnabled: addedByUser,
Ephemeral: ephemeral,
}
if account.Settings.Extra != nil {
newPeer = additions.PreparePeer(newPeer, account.Settings.Extra)
}
// add peer to 'All' group
group, err := account.GetGroupAll()
if err != nil {
@@ -456,8 +459,6 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *nbpeer.P
}
}
newPeer = am.integratedPeerValidator.PreparePeer(account.Id, newPeer, account.GetPeerGroupsList(newPeer.ID), account.Settings.Extra)
if addedByUser {
user, err := account.FindUser(userID)
if err != nil {
@@ -520,17 +521,6 @@ func (am *DefaultAccountManager) SyncPeer(sync PeerSync) (*nbpeer.Peer, *Network
if peerLoginExpired(peer, account) {
return nil, nil, status.Errorf(status.PermissionDenied, "peer login has expired, please log in once more")
}
requiresApproval, requiresIntegratedApproval := am.integratedPeerValidator.IsRequiresApproval(account.Id, peer, account.GetPeerGroupsList(peer.ID), account.Settings.Extra)
if peer.Status.RequiresApproval != requiresApproval || peer.Status.RequiresIntegratedApproval != requiresIntegratedApproval {
peer.Status.RequiresApproval = requiresApproval
peer.Status.RequiresIntegratedApproval = requiresIntegratedApproval
err = am.Store.SaveAccount(account)
if err != nil {
return nil, nil, err
}
}
return peer, account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
}
@@ -597,13 +587,6 @@ func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*nbpeer.Peer, *Netw
am.StoreEvent(login.UserID, peer.ID, account.Id, activity.UserLoggedInPeer, peer.EventMeta(am.GetDNSDomain()))
}
isRequiresApproval, isRequiresIntegratedApproval := am.integratedPeerValidator.IsRequiresApproval(account.Id, peer, account.GetPeerGroupsList(peer.ID), account.Settings.Extra)
if peer.Status.RequiresApproval != isRequiresApproval || peer.Status.RequiresIntegratedApproval != isRequiresIntegratedApproval {
peer.Status.RequiresApproval = isRequiresApproval
peer.Status.RequiresIntegratedApproval = isRequiresIntegratedApproval
shouldStoreAccount = true
}
peer, updated := updatePeerMeta(peer, login.Meta, account)
if updated {
shouldStoreAccount = true
@@ -624,7 +607,6 @@ func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*nbpeer.Peer, *Netw
if updateRemotePeers {
am.updateAccountPeers(account)
}
return peer, account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
}

View File

@@ -40,15 +40,13 @@ type Peer struct {
LoginExpirationEnabled bool
// LastLogin the time when peer performed last login operation
LastLogin time.Time
// CreatedAt records the time the peer was created
CreatedAt time.Time
// Indicate ephemeral peer attribute
Ephemeral bool
// Geo location based on connection IP
Location Location `gorm:"embedded;embeddedPrefix:location_"`
}
type PeerStatus struct { //nolint:revive
type PeerStatus struct {
// LastSeen is the last time peer was connected to the management service
LastSeen time.Time
// Connected indicates whether peer is connected to the management service or not
@@ -57,8 +55,6 @@ type PeerStatus struct { //nolint:revive
LoginExpired bool
// RequiresApproval indicates whether peer requires approval or not
RequiresApproval bool
// RequiresIntegratedApproval indicates whether peer requires integrated approval or not
RequiresIntegratedApproval bool
}
// Location is a geo location information of a Peer based on public connection IP
@@ -75,14 +71,8 @@ type NetworkAddress struct {
Mac string
}
// Environment is a system environment information
type Environment struct {
Cloud string
Platform string
}
// PeerSystemMeta is a metadata of a Peer machine system
type PeerSystemMeta struct { //nolint:revive
type PeerSystemMeta struct {
Hostname string
GoOS string
Kernel string
@@ -97,7 +87,6 @@ type PeerSystemMeta struct { //nolint:revive
SystemSerialNumber string
SystemProductName string
SystemManufacturer string
Environment Environment `gorm:"serializer:json"`
}
func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool {
@@ -130,9 +119,7 @@ func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool {
p.UIVersion == other.UIVersion &&
p.SystemSerialNumber == other.SystemSerialNumber &&
p.SystemProductName == other.SystemProductName &&
p.SystemManufacturer == other.SystemManufacturer &&
p.Environment.Cloud == other.Environment.Cloud &&
p.Environment.Platform == other.Environment.Platform
p.SystemManufacturer == other.SystemManufacturer
}
// AddedWithSSOLogin indicates whether this peer has been added with an SSO login by a user.
@@ -161,7 +148,6 @@ func (p *Peer) Copy() *Peer {
SSHEnabled: p.SSHEnabled,
LoginExpirationEnabled: p.LoginExpirationEnabled,
LastLogin: p.LastLogin,
CreatedAt: p.CreatedAt,
Ephemeral: p.Ephemeral,
Location: p.Location,
}
@@ -218,17 +204,16 @@ func (p *Peer) FQDN(dnsDomain string) string {
// EventMeta returns activity event meta related to the peer
func (p *Peer) EventMeta(dnsDomain string) map[string]any {
return map[string]any{"name": p.Name, "fqdn": p.FQDN(dnsDomain), "ip": p.IP, "created_at": p.CreatedAt}
return map[string]any{"name": p.Name, "fqdn": p.FQDN(dnsDomain), "ip": p.IP}
}
// Copy PeerStatus
func (p *PeerStatus) Copy() *PeerStatus {
return &PeerStatus{
LastSeen: p.LastSeen,
Connected: p.Connected,
LoginExpired: p.LoginExpired,
RequiresApproval: p.RequiresApproval,
RequiresIntegratedApproval: p.RequiresIntegratedApproval,
LastSeen: p.LastSeen,
Connected: p.Connected,
LoginExpired: p.LoginExpired,
RequiresApproval: p.RequiresApproval,
}
}

View File

@@ -5,9 +5,8 @@ import (
"strconv"
"strings"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/management-integrations/additions"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/activity"

View File

@@ -10,10 +10,10 @@ import (
)
const (
NBVersionCheckName = "NBVersionCheck"
OSVersionCheckName = "OSVersionCheck"
GeoLocationCheckName = "GeoLocationCheck"
PeerNetworkRangeCheckName = "PeerNetworkRangeCheck"
NBVersionCheckName = "NBVersionCheck"
OSVersionCheckName = "OSVersionCheck"
GeoLocationCheckName = "GeoLocationCheck"
PrivateNetworkCheckName = "PrivateNetworkCheck"
CheckActionAllow string = "allow"
CheckActionDeny string = "deny"
@@ -44,10 +44,10 @@ type Checks struct {
// ChecksDefinition contains definition of actual check
type ChecksDefinition struct {
NBVersionCheck *NBVersionCheck `json:",omitempty"`
OSVersionCheck *OSVersionCheck `json:",omitempty"`
GeoLocationCheck *GeoLocationCheck `json:",omitempty"`
PeerNetworkRangeCheck *PeerNetworkRangeCheck `json:",omitempty"`
NBVersionCheck *NBVersionCheck `json:",omitempty"`
OSVersionCheck *OSVersionCheck `json:",omitempty"`
GeoLocationCheck *GeoLocationCheck `json:",omitempty"`
PrivateNetworkCheck *PrivateNetworkCheck `json:",omitempty"`
}
// Copy returns a copy of a checks definition.
@@ -85,13 +85,13 @@ func (cd ChecksDefinition) Copy() ChecksDefinition {
}
copy(cdCopy.GeoLocationCheck.Locations, geoCheck.Locations)
}
if cd.PeerNetworkRangeCheck != nil {
peerNetRangeCheck := cd.PeerNetworkRangeCheck
cdCopy.PeerNetworkRangeCheck = &PeerNetworkRangeCheck{
Action: peerNetRangeCheck.Action,
Ranges: make([]netip.Prefix, len(peerNetRangeCheck.Ranges)),
if cd.PrivateNetworkCheck != nil {
privateNetCheck := cd.PrivateNetworkCheck
cdCopy.PrivateNetworkCheck = &PrivateNetworkCheck{
Action: privateNetCheck.Action,
Ranges: make([]netip.Prefix, len(privateNetCheck.Ranges)),
}
copy(cdCopy.PeerNetworkRangeCheck.Ranges, peerNetRangeCheck.Ranges)
copy(cdCopy.PrivateNetworkCheck.Ranges, privateNetCheck.Ranges)
}
return cdCopy
}
@@ -130,8 +130,8 @@ func (pc *Checks) GetChecks() []Check {
if pc.Checks.GeoLocationCheck != nil {
checks = append(checks, pc.Checks.GeoLocationCheck)
}
if pc.Checks.PeerNetworkRangeCheck != nil {
checks = append(checks, pc.Checks.PeerNetworkRangeCheck)
if pc.Checks.PrivateNetworkCheck != nil {
checks = append(checks, pc.Checks.PrivateNetworkCheck)
}
return checks
}

View File

@@ -254,7 +254,7 @@ func TestChecks_Copy(t *testing.T) {
},
Action: CheckActionAllow,
},
PeerNetworkRangeCheck: &PeerNetworkRangeCheck{
PrivateNetworkCheck: &PrivateNetworkCheck{
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/24"),
netip.MustParsePrefix("10.0.0.0/8"),

View File

@@ -8,16 +8,16 @@ import (
nbpeer "github.com/netbirdio/netbird/management/server/peer"
)
type PeerNetworkRangeCheck struct {
type PrivateNetworkCheck struct {
Action string
Ranges []netip.Prefix `gorm:"serializer:json"`
}
var _ Check = (*PeerNetworkRangeCheck)(nil)
var _ Check = (*PrivateNetworkCheck)(nil)
func (p *PeerNetworkRangeCheck) Check(peer nbpeer.Peer) (bool, error) {
func (p *PrivateNetworkCheck) Check(peer nbpeer.Peer) (bool, error) {
if len(peer.Meta.NetworkAddresses) == 0 {
return false, fmt.Errorf("peer's does not contain peer network range addresses")
return false, fmt.Errorf("peer's does not contain private network addresses")
}
maskedPrefixes := make([]netip.Prefix, 0, len(p.Ranges))
@@ -34,7 +34,7 @@ func (p *PeerNetworkRangeCheck) Check(peer nbpeer.Peer) (bool, error) {
case CheckActionAllow:
return true, nil
default:
return false, fmt.Errorf("invalid peer network range check action: %s", p.Action)
return false, fmt.Errorf("invalid private network check action: %s", p.Action)
}
}
}
@@ -46,9 +46,9 @@ func (p *PeerNetworkRangeCheck) Check(peer nbpeer.Peer) (bool, error) {
return false, nil
}
return false, fmt.Errorf("invalid peer network range check action: %s", p.Action)
return false, fmt.Errorf("invalid private network check action: %s", p.Action)
}
func (p *PeerNetworkRangeCheck) Name() string {
return PeerNetworkRangeCheckName
func (p *PrivateNetworkCheck) Name() string {
return PrivateNetworkCheckName
}

View File

@@ -9,17 +9,17 @@ import (
nbpeer "github.com/netbirdio/netbird/management/server/peer"
)
func TestPeerNetworkRangeCheck_Check(t *testing.T) {
func TestPrivateNetworkCheck_Check(t *testing.T) {
tests := []struct {
name string
check PeerNetworkRangeCheck
check PrivateNetworkCheck
peer nbpeer.Peer
wantErr bool
isValid bool
}{
{
name: "Peer networks range matches the allowed range",
check: PeerNetworkRangeCheck{
name: "Peer private networks matches the allowed range",
check: PrivateNetworkCheck{
Action: CheckActionAllow,
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/24"),
@@ -42,8 +42,8 @@ func TestPeerNetworkRangeCheck_Check(t *testing.T) {
isValid: true,
},
{
name: "Peer networks range doesn't matches the allowed range",
check: PeerNetworkRangeCheck{
name: "Peer private networks doesn't matches the allowed range",
check: PrivateNetworkCheck{
Action: CheckActionAllow,
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/24"),
@@ -63,8 +63,8 @@ func TestPeerNetworkRangeCheck_Check(t *testing.T) {
isValid: false,
},
{
name: "Peer with no network range in the allow range",
check: PeerNetworkRangeCheck{
name: "Peer with no privates network in the allow range",
check: PrivateNetworkCheck{
Action: CheckActionAllow,
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/16"),
@@ -76,8 +76,8 @@ func TestPeerNetworkRangeCheck_Check(t *testing.T) {
isValid: false,
},
{
name: "Peer networks range matches the denied range",
check: PeerNetworkRangeCheck{
name: "Peer private networks matches the denied range",
check: PrivateNetworkCheck{
Action: CheckActionDeny,
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/24"),
@@ -100,8 +100,8 @@ func TestPeerNetworkRangeCheck_Check(t *testing.T) {
isValid: false,
},
{
name: "Peer networks range doesn't matches the denied range",
check: PeerNetworkRangeCheck{
name: "Peer private networks doesn't matches the denied range",
check: PrivateNetworkCheck{
Action: CheckActionDeny,
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/24"),
@@ -121,8 +121,8 @@ func TestPeerNetworkRangeCheck_Check(t *testing.T) {
isValid: true,
},
{
name: "Peer with no networks range in the denied range",
check: PeerNetworkRangeCheck{
name: "Peer with no private networks in the denied range",
check: PrivateNetworkCheck{
Action: CheckActionDeny,
Ranges: []netip.Prefix{
netip.MustParsePrefix("192.168.0.0/16"),

View File

@@ -1014,7 +1014,7 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
return nil, err
}
eventStore := &activity.InMemoryEventStore{}
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "", eventStore, nil, false, MocIntegratedApproval{})
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "", eventStore, nil, false)
}
func createRouterStore(t *testing.T) (Store, error) {

View File

@@ -1,10 +1,9 @@
package server
import (
log "github.com/sirupsen/logrus"
"sync"
"time"
log "github.com/sirupsen/logrus"
)
// Scheduler is an interface which implementations can schedule and cancel jobs
@@ -56,8 +55,14 @@ func (wm *DefaultScheduler) cancel(ID string) bool {
cancel, ok := wm.jobs[ID]
if ok {
delete(wm.jobs, ID)
close(cancel)
log.Debugf("cancelled scheduled job %s", ID)
select {
case cancel <- struct{}{}:
log.Debugf("cancelled scheduled job %s", ID)
default:
log.Warnf("couldn't cancel job %s because there was no routine listening on the cancel event", ID)
return false
}
}
return ok
}
@@ -85,41 +90,25 @@ func (wm *DefaultScheduler) Schedule(in time.Duration, ID string, job func() (ne
return
}
ticker := time.NewTicker(in)
wm.jobs[ID] = cancel
log.Debugf("scheduled a job %s to run in %s. There are %d total jobs scheduled.", ID, in.String(), len(wm.jobs))
go func() {
for {
select {
case <-ticker.C:
select {
case <-cancel:
log.Debugf("scheduled job %s was canceled, stop timer", ID)
ticker.Stop()
return
default:
log.Debugf("time to do a scheduled job %s", ID)
}
runIn, reschedule := job()
if !reschedule {
wm.mu.Lock()
defer wm.mu.Unlock()
delete(wm.jobs, ID)
log.Debugf("job %s is not scheduled to run again", ID)
ticker.Stop()
return
}
// we need this comparison to avoid resetting the ticker with the same duration and missing the current elapsesed time
if runIn != in {
ticker.Reset(runIn)
}
case <-cancel:
log.Debugf("job %s was canceled, stopping timer", ID)
ticker.Stop()
return
select {
case <-time.After(in):
log.Debugf("time to do a scheduled job %s", ID)
runIn, reschedule := job()
wm.mu.Lock()
defer wm.mu.Unlock()
delete(wm.jobs, ID)
if reschedule {
go wm.Schedule(runIn, ID, job)
}
case <-cancel:
log.Debugf("stopped scheduled job %s ", ID)
wm.mu.Lock()
defer wm.mu.Unlock()
delete(wm.jobs, ID)
return
}
}()
}

View File

@@ -2,12 +2,11 @@ package server
import (
"fmt"
"github.com/stretchr/testify/assert"
"math/rand"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestScheduler_Performance(t *testing.T) {
@@ -37,24 +36,15 @@ func TestScheduler_Cancel(t *testing.T) {
jobID1 := "test-scheduler-job-1"
jobID2 := "test-scheduler-job-2"
scheduler := NewDefaultScheduler()
tChan := make(chan struct{})
p := []string{jobID1, jobID2}
scheduler.Schedule(2*time.Millisecond, jobID1, func() (nextRunIn time.Duration, reschedule bool) {
tt := p[0]
<-tChan
t.Logf("job %s", tt)
return 2 * time.Millisecond, true
scheduler.Schedule(2*time.Second, jobID1, func() (nextRunIn time.Duration, reschedule bool) {
return 0, false
})
scheduler.Schedule(2*time.Millisecond, jobID2, func() (nextRunIn time.Duration, reschedule bool) {
return 2 * time.Millisecond, true
scheduler.Schedule(2*time.Second, jobID2, func() (nextRunIn time.Duration, reschedule bool) {
return 0, false
})
time.Sleep(4 * time.Millisecond)
assert.Len(t, scheduler.jobs, 2)
scheduler.Cancel([]string{jobID1})
close(tChan)
p = []string{}
time.Sleep(4 * time.Millisecond)
assert.Len(t, scheduler.jobs, 1)
assert.NotNil(t, scheduler.jobs[jobID2])
}

View File

@@ -85,8 +85,6 @@ type User struct {
Blocked bool
// LastLogin is the last time the user logged in to IdP
LastLogin time.Time
// CreatedAt records the time the user was created
CreatedAt time.Time
// Issued of the user
Issued string `gorm:"default:api"`
@@ -175,7 +173,6 @@ func (u *User) Copy() *User {
PATs: pats,
Blocked: u.Blocked,
LastLogin: u.LastLogin,
CreatedAt: u.CreatedAt,
Issued: u.Issued,
IntegrationReference: u.IntegrationReference,
}
@@ -191,7 +188,6 @@ func NewUser(id string, role UserRole, isServiceUser bool, nonDeletable bool, se
ServiceUserName: serviceUserName,
AutoGroups: autoGroups,
Issued: issued,
CreatedAt: time.Now().UTC(),
}
}
@@ -342,7 +338,6 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite
AutoGroups: invite.AutoGroups,
Issued: invite.Issued,
IntegrationReference: invite.IntegrationReference,
CreatedAt: time.Now().UTC(),
}
account.Users[idpUser.ID] = newUser
@@ -419,7 +414,7 @@ func (am *DefaultAccountManager) ListUsers(accountID string) ([]*User, error) {
}
func (am *DefaultAccountManager) deleteServiceUser(account *Account, initiatorUserID string, targetUser *User) {
meta := map[string]any{"name": targetUser.ServiceUserName, "created_at": targetUser.CreatedAt}
meta := map[string]any{"name": targetUser.ServiceUserName}
am.StoreEvent(initiatorUserID, targetUser.Id, account.Id, activity.ServiceUserDeleted, meta)
delete(account.Users, targetUser.Id)
}
@@ -499,23 +494,13 @@ func (am *DefaultAccountManager) deleteRegularUser(account *Account, initiatorUs
return err
}
u, err := account.FindUser(targetUserID)
if err != nil {
log.Errorf("failed to find user %s for deletion, this should never happen: %s", targetUserID, err)
}
var tuCreatedAt time.Time
if u != nil {
tuCreatedAt = u.CreatedAt
}
delete(account.Users, targetUserID)
err = am.Store.SaveAccount(account)
if err != nil {
return err
}
meta := map[string]any{"name": tuName, "email": tuEmail, "created_at": tuCreatedAt}
meta := map[string]any{"name": tuName, "email": tuEmail}
am.StoreEvent(initiatorUserID, targetUserID, account.Id, activity.UserDeleted, meta)
am.updateAccountPeers(account)

View File

@@ -273,8 +273,7 @@ func TestUser_Copy(t *testing.T) {
},
},
Blocked: false,
LastLogin: time.Now().UTC(),
CreatedAt: time.Now().UTC(),
LastLogin: time.Now(),
Issued: "test",
IntegrationReference: IntegrationReference{
ID: 0,

View File

@@ -21,10 +21,11 @@ import (
"google.golang.org/grpc/status"
"github.com/netbirdio/netbird/encryption"
"github.com/netbirdio/netbird/management/client"
"github.com/netbirdio/netbird/signal/proto"
)
const defaultSendTimeout = 5 * time.Second
// ConnStateNotifier is a wrapper interface of the status recorder
type ConnStateNotifier interface {
MarkSignalDisconnected(error)
@@ -70,7 +71,7 @@ func NewClient(ctx context.Context, addr string, key wgtypes.Key, tlsEnabled boo
transportOption = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))
}
sigCtx, cancel := context.WithTimeout(ctx, client.ConnectTimeout)
sigCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
conn, err := grpc.DialContext(
sigCtx,
@@ -352,7 +353,7 @@ func (c *GrpcClient) Send(msg *proto.Message) error {
return err
}
attemptTimeout := client.ConnectTimeout
attemptTimeout := defaultSendTimeout
for attempt := 0; attempt < 4; attempt++ {
if attempt > 1 {

View File

@@ -4,6 +4,7 @@ import (
"errors"
"flag"
"fmt"
"golang.org/x/crypto/acme/autocert"
"io"
"io/fs"
"net"
@@ -13,14 +14,10 @@ import (
"strings"
"time"
"golang.org/x/crypto/acme/autocert"
"github.com/netbirdio/netbird/encryption"
"github.com/netbirdio/netbird/signal/proto"
"github.com/netbirdio/netbird/signal/server"
"github.com/netbirdio/netbird/util"
"github.com/netbirdio/netbird/version"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc"
@@ -132,7 +129,6 @@ var (
log.Infof("running gRPC server: %s", grpcListener.Addr().String())
}
log.Infof("signal server version %s", version.NetbirdVersion())
log.Infof("started Signal Service")
SetupCloseHandler()