Merge branch 'main' into android-dns-routes

This commit is contained in:
Viktor Liu
2025-07-02 15:23:14 +02:00
65 changed files with 2091 additions and 2232 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ on:
pull_request: pull_request:
env: env:
SIGN_PIPE_VER: "v0.0.18" SIGN_PIPE_VER: "v0.0.20"
GORELEASER_VER: "v2.3.2" GORELEASER_VER: "v2.3.2"
PRODUCT_NAME: "NetBird" PRODUCT_NAME: "NetBird"
COPYRIGHT: "NetBird GmbH" COPYRIGHT: "NetBird GmbH"
@@ -134,6 +134,7 @@ jobs:
NETBIRD_STORE_ENGINE_MYSQL_DSN: '${{ env.NETBIRD_STORE_ENGINE_MYSQL_DSN }}$' NETBIRD_STORE_ENGINE_MYSQL_DSN: '${{ env.NETBIRD_STORE_ENGINE_MYSQL_DSN }}$'
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
CI_NETBIRD_TURN_EXTERNAL_IP: "1.2.3.4" CI_NETBIRD_TURN_EXTERNAL_IP: "1.2.3.4"
CI_NETBIRD_MGMT_DISABLE_DEFAULT_POLICY: false
run: | run: |
set -x set -x
@@ -180,6 +181,7 @@ jobs:
grep -A 7 Relay management.json | egrep '"Secret": ".+"' grep -A 7 Relay management.json | egrep '"Secret": ".+"'
grep DisablePromptLogin management.json | grep 'true' grep DisablePromptLogin management.json | grep 'true'
grep LoginFlag management.json | grep 0 grep LoginFlag management.json | grep 0
grep DisableDefaultPolicy management.json | grep "$CI_NETBIRD_MGMT_DISABLE_DEFAULT_POLICY"
- name: Install modules - name: Install modules
run: go mod tidy run: go mod tidy
+4 -1
View File
@@ -1,6 +1,9 @@
FROM alpine:3.21.3 FROM alpine:3.21.3
# iproute2: busybox doesn't display ip rules properly # iproute2: busybox doesn't display ip rules properly
RUN apk add --no-cache ca-certificates ip6tables iproute2 iptables RUN apk add --no-cache ca-certificates ip6tables iproute2 iptables
ARG NETBIRD_BINARY=netbird
COPY ${NETBIRD_BINARY} /usr/local/bin/netbird
ENV NB_FOREGROUND_MODE=true ENV NB_FOREGROUND_MODE=true
ENTRYPOINT [ "/usr/local/bin/netbird","up"] ENTRYPOINT [ "/usr/local/bin/netbird","up"]
COPY netbird /usr/local/bin/netbird
+2 -1
View File
@@ -1,6 +1,7 @@
FROM alpine:3.21.0 FROM alpine:3.21.0
COPY netbird /usr/local/bin/netbird ARG NETBIRD_BINARY=netbird
COPY ${NETBIRD_BINARY} /usr/local/bin/netbird
RUN apk add --no-cache ca-certificates \ RUN apk add --no-cache ca-certificates \
&& adduser -D -h /var/lib/netbird netbird && adduser -D -h /var/lib/netbird netbird
+1 -1
View File
@@ -120,7 +120,7 @@ func getStatus(ctx context.Context) (*proto.StatusResponse, error) {
} }
defer conn.Close() defer conn.Close()
resp, err := proto.NewDaemonServiceClient(conn).Status(ctx, &proto.StatusRequest{GetFullPeerStatus: true}) resp, err := proto.NewDaemonServiceClient(conn).Status(ctx, &proto.StatusRequest{GetFullPeerStatus: true, ShouldRunProbes: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("status failed: %v", status.Convert(err).Message()) return nil, fmt.Errorf("status failed: %v", status.Convert(err).Message())
} }
+1 -1
View File
@@ -103,7 +103,7 @@ func startManagement(t *testing.T, config *types.Config, testFile string) (*grpc
Return(&types.Settings{}, nil). Return(&types.Settings{}, nil).
AnyTimes() AnyTimes()
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock) accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+6 -2
View File
@@ -398,11 +398,15 @@ func (d *DefaultManager) squashAcceptRules(
// //
// We zeroed this to notify squash function that this protocol can't be squashed. // We zeroed this to notify squash function that this protocol can't be squashed.
addRuleToCalculationMap := func(i int, r *mgmProto.FirewallRule, protocols map[mgmProto.RuleProtocol]*protoMatch) { addRuleToCalculationMap := func(i int, r *mgmProto.FirewallRule, protocols map[mgmProto.RuleProtocol]*protoMatch) {
drop := r.Action == mgmProto.RuleAction_DROP || r.Port != "" hasPortRestrictions := r.Action == mgmProto.RuleAction_DROP ||
if drop { r.Port != "" || !portInfoEmpty(r.PortInfo)
if hasPortRestrictions {
// Don't squash rules with port restrictions
protocols[r.Protocol] = &protoMatch{ips: map[string]int{}} protocols[r.Protocol] = &protoMatch{ips: map[string]int{}}
return return
} }
if _, ok := protocols[r.Protocol]; !ok { if _, ok := protocols[r.Protocol]; !ok {
protocols[r.Protocol] = &protoMatch{ protocols[r.Protocol] = &protoMatch{
ips: map[string]int{}, ips: map[string]int{},
+428
View File
@@ -330,6 +330,434 @@ func TestDefaultManagerSquashRulesNoAffect(t *testing.T) {
assert.Equal(t, len(networkMap.FirewallRules), len(rules)) assert.Equal(t, len(networkMap.FirewallRules), len(rules))
} }
func TestDefaultManagerSquashRulesWithPortRestrictions(t *testing.T) {
tests := []struct {
name string
rules []*mgmProto.FirewallRule
expectedCount int
description string
}{
{
name: "should not squash rules with port ranges",
rules: []*mgmProto.FirewallRule{
{
PeerIP: "10.93.0.1",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Range_{
Range: &mgmProto.PortInfo_Range{
Start: 8080,
End: 8090,
},
},
},
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Range_{
Range: &mgmProto.PortInfo_Range{
Start: 8080,
End: 8090,
},
},
},
},
{
PeerIP: "10.93.0.3",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Range_{
Range: &mgmProto.PortInfo_Range{
Start: 8080,
End: 8090,
},
},
},
},
{
PeerIP: "10.93.0.4",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Range_{
Range: &mgmProto.PortInfo_Range{
Start: 8080,
End: 8090,
},
},
},
},
},
expectedCount: 4,
description: "Rules with port ranges should not be squashed even if they cover all peers",
},
{
name: "should not squash rules with specific ports",
rules: []*mgmProto.FirewallRule{
{
PeerIP: "10.93.0.1",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Port{
Port: 80,
},
},
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Port{
Port: 80,
},
},
},
{
PeerIP: "10.93.0.3",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Port{
Port: 80,
},
},
},
{
PeerIP: "10.93.0.4",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Port{
Port: 80,
},
},
},
},
expectedCount: 4,
description: "Rules with specific ports should not be squashed even if they cover all peers",
},
{
name: "should not squash rules with legacy port field",
rules: []*mgmProto.FirewallRule{
{
PeerIP: "10.93.0.1",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
Port: "443",
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
Port: "443",
},
{
PeerIP: "10.93.0.3",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
Port: "443",
},
{
PeerIP: "10.93.0.4",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
Port: "443",
},
},
expectedCount: 4,
description: "Rules with legacy port field should not be squashed",
},
{
name: "should not squash rules with DROP action",
rules: []*mgmProto.FirewallRule{
{
PeerIP: "10.93.0.1",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_DROP,
Protocol: mgmProto.RuleProtocol_TCP,
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_DROP,
Protocol: mgmProto.RuleProtocol_TCP,
},
{
PeerIP: "10.93.0.3",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_DROP,
Protocol: mgmProto.RuleProtocol_TCP,
},
{
PeerIP: "10.93.0.4",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_DROP,
Protocol: mgmProto.RuleProtocol_TCP,
},
},
expectedCount: 4,
description: "Rules with DROP action should not be squashed",
},
{
name: "should squash rules without port restrictions",
rules: []*mgmProto.FirewallRule{
{
PeerIP: "10.93.0.1",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
},
{
PeerIP: "10.93.0.3",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
},
{
PeerIP: "10.93.0.4",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
},
},
expectedCount: 1,
description: "Rules without port restrictions should be squashed into a single 0.0.0.0 rule",
},
{
name: "mixed rules should not squash protocol with port restrictions",
rules: []*mgmProto.FirewallRule{
{
PeerIP: "10.93.0.1",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
PortInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Port{
Port: 80,
},
},
},
{
PeerIP: "10.93.0.3",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
},
{
PeerIP: "10.93.0.4",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
},
},
expectedCount: 4,
description: "TCP should not be squashed because one rule has port restrictions",
},
{
name: "should squash UDP but not TCP when TCP has port restrictions",
rules: []*mgmProto.FirewallRule{
// TCP rules with port restrictions - should NOT be squashed
{
PeerIP: "10.93.0.1",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
Port: "443",
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
Port: "443",
},
{
PeerIP: "10.93.0.3",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
Port: "443",
},
{
PeerIP: "10.93.0.4",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_TCP,
Port: "443",
},
// UDP rules without port restrictions - SHOULD be squashed
{
PeerIP: "10.93.0.1",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_UDP,
},
{
PeerIP: "10.93.0.2",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_UDP,
},
{
PeerIP: "10.93.0.3",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_UDP,
},
{
PeerIP: "10.93.0.4",
Direction: mgmProto.RuleDirection_IN,
Action: mgmProto.RuleAction_ACCEPT,
Protocol: mgmProto.RuleProtocol_UDP,
},
},
expectedCount: 5, // 4 TCP rules + 1 squashed UDP rule (0.0.0.0)
description: "UDP should be squashed to 0.0.0.0 rule, but TCP should remain as individual rules due to port restrictions",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
networkMap := &mgmProto.NetworkMap{
RemotePeers: []*mgmProto.RemotePeerConfig{
{AllowedIps: []string{"10.93.0.1"}},
{AllowedIps: []string{"10.93.0.2"}},
{AllowedIps: []string{"10.93.0.3"}},
{AllowedIps: []string{"10.93.0.4"}},
},
FirewallRules: tt.rules,
}
manager := &DefaultManager{}
rules, _ := manager.squashAcceptRules(networkMap)
assert.Equal(t, tt.expectedCount, len(rules), tt.description)
// For squashed rules, verify we get the expected 0.0.0.0 rule
if tt.expectedCount == 1 {
assert.Equal(t, "0.0.0.0", rules[0].PeerIP)
assert.Equal(t, mgmProto.RuleDirection_IN, rules[0].Direction)
assert.Equal(t, mgmProto.RuleAction_ACCEPT, rules[0].Action)
}
})
}
}
func TestPortInfoEmpty(t *testing.T) {
tests := []struct {
name string
portInfo *mgmProto.PortInfo
expected bool
}{
{
name: "nil PortInfo should be empty",
portInfo: nil,
expected: true,
},
{
name: "PortInfo with zero port should be empty",
portInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Port{
Port: 0,
},
},
expected: true,
},
{
name: "PortInfo with valid port should not be empty",
portInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Port{
Port: 80,
},
},
expected: false,
},
{
name: "PortInfo with nil range should be empty",
portInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Range_{
Range: nil,
},
},
expected: true,
},
{
name: "PortInfo with zero start range should be empty",
portInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Range_{
Range: &mgmProto.PortInfo_Range{
Start: 0,
End: 100,
},
},
},
expected: true,
},
{
name: "PortInfo with zero end range should be empty",
portInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Range_{
Range: &mgmProto.PortInfo_Range{
Start: 80,
End: 0,
},
},
},
expected: true,
},
{
name: "PortInfo with valid range should not be empty",
portInfo: &mgmProto.PortInfo{
PortSelection: &mgmProto.PortInfo_Range_{
Range: &mgmProto.PortInfo_Range{
Start: 8080,
End: 8090,
},
},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := portInfoEmpty(tt.portInfo)
assert.Equal(t, tt.expected, result)
})
}
}
func TestDefaultManagerEnableSSHRules(t *testing.T) { func TestDefaultManagerEnableSSHRules(t *testing.T) {
networkMap := &mgmProto.NetworkMap{ networkMap := &mgmProto.NetworkMap{
PeerConfig: &mgmProto.PeerConfig{ PeerConfig: &mgmProto.PeerConfig{
-4
View File
@@ -319,10 +319,6 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
*input.WireguardPort, config.WgPort) *input.WireguardPort, config.WgPort)
config.WgPort = *input.WireguardPort config.WgPort = *input.WireguardPort
updated = true updated = true
} else if config.WgPort == 0 {
config.WgPort = iface.DefaultWgPort
log.Infof("using default Wireguard port %d", config.WgPort)
updated = true
} }
if input.InterfaceName != nil && *input.InterfaceName != config.WgIface { if input.InterfaceName != nil && *input.InterfaceName != config.WgIface {
+1 -1
View File
@@ -175,7 +175,7 @@ func (e *ConnMgr) AddPeerConn(ctx context.Context, peerKey string, conn *peer.Co
PeerConnID: conn.ConnID(), PeerConnID: conn.ConnID(),
Log: conn.Log, Log: conn.Log,
} }
excluded, err := e.lazyConnMgr.AddPeer(lazyPeerCfg) excluded, err := e.lazyConnMgr.AddPeer(e.lazyCtx, lazyPeerCfg)
if err != nil { if err != nil {
conn.Log.Errorf("failed to add peer to lazyconn manager: %v", err) conn.Log.Errorf("failed to add peer to lazyconn manager: %v", err)
if err := conn.Open(ctx); err != nil { if err := conn.Open(ctx); err != nil {
+3 -8
View File
@@ -17,7 +17,6 @@ import (
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status" gstatus "google.golang.org/grpc/status"
"github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/dns"
"github.com/netbirdio/netbird/client/internal/listener" "github.com/netbirdio/netbird/client/internal/listener"
@@ -526,17 +525,13 @@ func statusRecorderToSignalConnStateNotifier(statusRecorder *peer.Status) signal
// freePort attempts to determine if the provided port is available, if not it will ask the system for a free port. // freePort attempts to determine if the provided port is available, if not it will ask the system for a free port.
func freePort(initPort int) (int, error) { func freePort(initPort int) (int, error) {
addr := net.UDPAddr{} addr := net.UDPAddr{Port: initPort}
if initPort == 0 {
initPort = iface.DefaultWgPort
}
addr.Port = initPort
conn, err := net.ListenUDP("udp", &addr) conn, err := net.ListenUDP("udp", &addr)
if err == nil { if err == nil {
returnPort := conn.LocalAddr().(*net.UDPAddr).Port
closeConnWithLog(conn) closeConnWithLog(conn)
return initPort, nil return returnPort, nil
} }
// if the port is already in use, ask the system for a free port // if the port is already in use, ask the system for a free port
+12 -4
View File
@@ -13,10 +13,10 @@ func Test_freePort(t *testing.T) {
shouldMatch bool shouldMatch bool
}{ }{
{ {
name: "not provided, fallback to default", name: "when port is 0 use random port",
port: 0, port: 0,
want: 51820, want: 0,
shouldMatch: true, shouldMatch: false,
}, },
{ {
name: "provided and available", name: "provided and available",
@@ -31,7 +31,7 @@ func Test_freePort(t *testing.T) {
shouldMatch: false, shouldMatch: false,
}, },
} }
c1, err := net.ListenUDP("udp", &net.UDPAddr{Port: 51830}) c1, err := net.ListenUDP("udp", &net.UDPAddr{Port: 0})
if err != nil { if err != nil {
t.Errorf("freePort error = %v", err) t.Errorf("freePort error = %v", err)
} }
@@ -39,6 +39,14 @@ func Test_freePort(t *testing.T) {
_ = c1.Close() _ = c1.Close()
}(c1) }(c1)
if tests[1].port == c1.LocalAddr().(*net.UDPAddr).Port {
tests[1].port++
tests[1].want++
}
tests[2].port = c1.LocalAddr().(*net.UDPAddr).Port
tests[2].want = c1.LocalAddr().(*net.UDPAddr).Port
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
+1 -1
View File
@@ -1476,7 +1476,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
permissionsManager := permissions.NewManager(store) permissionsManager := permissions.NewManager(store)
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
@@ -68,3 +68,8 @@ func (i *Monitor) PauseTimer() {
func (i *Monitor) ResetTimer() { func (i *Monitor) ResetTimer() {
i.timer.Reset(i.inactivityThreshold) i.timer.Reset(i.inactivityThreshold)
} }
func (i *Monitor) ResetMonitor(ctx context.Context, timeoutChan chan peer.ConnID) {
i.Stop()
go i.Start(ctx, timeoutChan)
}
+128 -21
View File
@@ -58,7 +58,7 @@ type Manager struct {
// Route HA group management // Route HA group management
peerToHAGroups map[string][]route.HAUniqueID // peer ID -> HA groups they belong to peerToHAGroups map[string][]route.HAUniqueID // peer ID -> HA groups they belong to
haGroupToPeers map[route.HAUniqueID][]string // HA group -> peer IDs in the group haGroupToPeers map[route.HAUniqueID][]string // HA group -> peer IDs in the group
routesMu sync.RWMutex // protects route mappings routesMu sync.RWMutex
onInactive chan peerid.ConnID onInactive chan peerid.ConnID
} }
@@ -146,7 +146,7 @@ func (m *Manager) Start(ctx context.Context) {
case peerConnID := <-m.activityManager.OnActivityChan: case peerConnID := <-m.activityManager.OnActivityChan:
m.onPeerActivity(ctx, peerConnID) m.onPeerActivity(ctx, peerConnID)
case peerConnID := <-m.onInactive: case peerConnID := <-m.onInactive:
m.onPeerInactivityTimedOut(peerConnID) m.onPeerInactivityTimedOut(ctx, peerConnID)
} }
} }
} }
@@ -197,7 +197,7 @@ func (m *Manager) ExcludePeer(ctx context.Context, peerConfigs []lazyconn.PeerCo
return added return added
} }
func (m *Manager) AddPeer(peerCfg lazyconn.PeerConfig) (bool, error) { func (m *Manager) AddPeer(ctx context.Context, peerCfg lazyconn.PeerConfig) (bool, error) {
m.managedPeersMu.Lock() m.managedPeersMu.Lock()
defer m.managedPeersMu.Unlock() defer m.managedPeersMu.Unlock()
@@ -225,6 +225,13 @@ func (m *Manager) AddPeer(peerCfg lazyconn.PeerConfig) (bool, error) {
peerCfg: &peerCfg, peerCfg: &peerCfg,
expectedWatcher: watcherActivity, expectedWatcher: watcherActivity,
} }
// Check if this peer should be activated because its HA group peers are active
if group, ok := m.shouldActivateNewPeer(peerCfg.PublicKey); ok {
peerCfg.Log.Debugf("peer belongs to active HA group %s, will activate immediately", group)
m.activateNewPeerInActiveGroup(ctx, peerCfg)
}
return false, nil return false, nil
} }
@@ -315,36 +322,38 @@ func (m *Manager) activateSinglePeer(ctx context.Context, cfg *lazyconn.PeerConf
// activateHAGroupPeers activates all peers in HA groups that the given peer belongs to // activateHAGroupPeers activates all peers in HA groups that the given peer belongs to
func (m *Manager) activateHAGroupPeers(ctx context.Context, triggerPeerID string) { func (m *Manager) activateHAGroupPeers(ctx context.Context, triggerPeerID string) {
var peersToActivate []string
m.routesMu.RLock() m.routesMu.RLock()
haGroups := m.peerToHAGroups[triggerPeerID] haGroups := m.peerToHAGroups[triggerPeerID]
m.routesMu.RUnlock()
if len(haGroups) == 0 { if len(haGroups) == 0 {
m.routesMu.RUnlock()
log.Debugf("peer %s is not part of any HA groups", triggerPeerID) log.Debugf("peer %s is not part of any HA groups", triggerPeerID)
return return
} }
activatedCount := 0
for _, haGroup := range haGroups { for _, haGroup := range haGroups {
m.routesMu.RLock()
peers := m.haGroupToPeers[haGroup] peers := m.haGroupToPeers[haGroup]
m.routesMu.RUnlock()
for _, peerID := range peers { for _, peerID := range peers {
if peerID == triggerPeerID { if peerID != triggerPeerID {
continue peersToActivate = append(peersToActivate, peerID)
} }
}
}
m.routesMu.RUnlock()
cfg, mp := m.getPeerForActivation(peerID) activatedCount := 0
if cfg == nil { for _, peerID := range peersToActivate {
continue cfg, mp := m.getPeerForActivation(peerID)
} if cfg == nil {
continue
}
if m.activateSinglePeer(ctx, cfg, mp) { if m.activateSinglePeer(ctx, cfg, mp) {
activatedCount++ activatedCount++
cfg.Log.Infof("activated peer as part of HA group %s (triggered by %s)", haGroup, triggerPeerID) cfg.Log.Infof("activated peer as part of HA group (triggered by %s)", triggerPeerID)
m.peerStore.PeerConnOpen(m.engineCtx, cfg.PublicKey) m.peerStore.PeerConnOpen(m.engineCtx, cfg.PublicKey)
}
} }
} }
@@ -354,6 +363,51 @@ func (m *Manager) activateHAGroupPeers(ctx context.Context, triggerPeerID string
} }
} }
// shouldActivateNewPeer checks if a newly added peer should be activated
// because other peers in its HA groups are already active
func (m *Manager) shouldActivateNewPeer(peerID string) (route.HAUniqueID, bool) {
m.routesMu.RLock()
defer m.routesMu.RUnlock()
haGroups := m.peerToHAGroups[peerID]
if len(haGroups) == 0 {
return "", false
}
for _, haGroup := range haGroups {
peers := m.haGroupToPeers[haGroup]
for _, groupPeerID := range peers {
if groupPeerID == peerID {
continue
}
cfg, ok := m.managedPeers[groupPeerID]
if !ok {
continue
}
if mp, ok := m.managedPeersByConnID[cfg.PeerConnID]; ok && mp.expectedWatcher == watcherInactivity {
return haGroup, true
}
}
}
return "", false
}
// activateNewPeerInActiveGroup activates a newly added peer that should be active due to HA group
func (m *Manager) activateNewPeerInActiveGroup(ctx context.Context, peerCfg lazyconn.PeerConfig) {
mp, ok := m.managedPeersByConnID[peerCfg.PeerConnID]
if !ok {
return
}
if !m.activateSinglePeer(ctx, &peerCfg, mp) {
return
}
peerCfg.Log.Infof("activated newly added peer due to active HA group peers")
m.peerStore.PeerConnOpen(m.engineCtx, peerCfg.PublicKey)
}
func (m *Manager) addActivePeer(ctx context.Context, peerCfg lazyconn.PeerConfig) error { func (m *Manager) addActivePeer(ctx context.Context, peerCfg lazyconn.PeerConfig) error {
if _, ok := m.managedPeers[peerCfg.PublicKey]; ok { if _, ok := m.managedPeers[peerCfg.PublicKey]; ok {
peerCfg.Log.Warnf("peer already managed") peerCfg.Log.Warnf("peer already managed")
@@ -415,6 +469,48 @@ func (m *Manager) close() {
log.Infof("lazy connection manager closed") log.Infof("lazy connection manager closed")
} }
// shouldDeferIdleForHA checks if peer should stay connected due to HA group requirements
func (m *Manager) shouldDeferIdleForHA(peerID string) bool {
m.routesMu.RLock()
defer m.routesMu.RUnlock()
haGroups := m.peerToHAGroups[peerID]
if len(haGroups) == 0 {
return false
}
for _, haGroup := range haGroups {
groupPeers := m.haGroupToPeers[haGroup]
for _, groupPeerID := range groupPeers {
if groupPeerID == peerID {
continue
}
cfg, ok := m.managedPeers[groupPeerID]
if !ok {
continue
}
groupMp, ok := m.managedPeersByConnID[cfg.PeerConnID]
if !ok {
continue
}
if groupMp.expectedWatcher != watcherInactivity {
continue
}
// Other member is still connected, defer idle
if peer, ok := m.peerStore.PeerConn(groupPeerID); ok && peer.IsConnected() {
return true
}
}
}
return false
}
func (m *Manager) onPeerActivity(ctx context.Context, peerConnID peerid.ConnID) { func (m *Manager) onPeerActivity(ctx context.Context, peerConnID peerid.ConnID) {
m.managedPeersMu.Lock() m.managedPeersMu.Lock()
defer m.managedPeersMu.Unlock() defer m.managedPeersMu.Unlock()
@@ -441,7 +537,7 @@ func (m *Manager) onPeerActivity(ctx context.Context, peerConnID peerid.ConnID)
m.peerStore.PeerConnOpen(m.engineCtx, mp.peerCfg.PublicKey) m.peerStore.PeerConnOpen(m.engineCtx, mp.peerCfg.PublicKey)
} }
func (m *Manager) onPeerInactivityTimedOut(peerConnID peerid.ConnID) { func (m *Manager) onPeerInactivityTimedOut(ctx context.Context, peerConnID peerid.ConnID) {
m.managedPeersMu.Lock() m.managedPeersMu.Lock()
defer m.managedPeersMu.Unlock() defer m.managedPeersMu.Unlock()
@@ -456,6 +552,17 @@ func (m *Manager) onPeerInactivityTimedOut(peerConnID peerid.ConnID) {
return return
} }
if m.shouldDeferIdleForHA(mp.peerCfg.PublicKey) {
iw, ok := m.inactivityMonitors[peerConnID]
if ok {
mp.peerCfg.Log.Debugf("resetting inactivity timer due to HA group requirements")
iw.ResetMonitor(ctx, m.onInactive)
} else {
mp.peerCfg.Log.Errorf("inactivity monitor not found for HA defer reset")
}
return
}
mp.peerCfg.Log.Infof("connection timed out") mp.peerCfg.Log.Infof("connection timed out")
// this is blocking operation, potentially can be optimized // this is blocking operation, potentially can be optimized
@@ -489,7 +596,7 @@ func (m *Manager) onPeerConnected(peerConnID peerid.ConnID) {
iw, ok := m.inactivityMonitors[mp.peerCfg.PeerConnID] iw, ok := m.inactivityMonitors[mp.peerCfg.PeerConnID]
if !ok { if !ok {
mp.peerCfg.Log.Errorf("inactivity monitor not found for peer") mp.peerCfg.Log.Warnf("inactivity monitor not found for peer")
return return
} }
+3 -3
View File
@@ -317,12 +317,12 @@ func (conn *Conn) WgConfig() WgConfig {
return conn.config.WgConfig return conn.config.WgConfig
} }
// IsConnected unit tests only // IsConnected returns true if the peer is connected
// refactor unit test to use status recorder use refactor status recorded to manage connection status in peer.Conn
func (conn *Conn) IsConnected() bool { func (conn *Conn) IsConnected() bool {
conn.mu.Lock() conn.mu.Lock()
defer conn.mu.Unlock() defer conn.mu.Unlock()
return conn.currentConnPriority != conntype.None
return conn.evalStatus() == StatusConnected
} }
func (conn *Conn) GetKey() string { func (conn *Conn) GetKey() string {
@@ -28,7 +28,10 @@ func (n Nexthop) String() string {
if n.Intf == nil { if n.Intf == nil {
return n.IP.String() return n.IP.String()
} }
return fmt.Sprintf("%s @ %d (%s)", n.IP.String(), n.Intf.Index, n.Intf.Name) if n.IP.IsValid() {
return fmt.Sprintf("%s @ %d (%s)", n.IP.String(), n.Intf.Index, n.Intf.Name)
}
return fmt.Sprintf("no-ip @ %d (%s)", n.Intf.Index, n.Intf.Name)
} }
type wgIface interface { type wgIface interface {
+5 -22
View File
@@ -1,8 +1,10 @@
<Wix <Wix
xmlns="http://wixtoolset.org/schemas/v4/wxs"> xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
<Package Name="NetBird" Version="$(env.NETBIRD_VERSION)" Manufacturer="NetBird GmbH" Language="1033" UpgradeCode="6456ec4e-3ad6-4b9b-a2be-98e81cb21ccf" <Package Name="NetBird" Version="$(env.NETBIRD_VERSION)" Manufacturer="NetBird GmbH" Language="1033" UpgradeCode="6456ec4e-3ad6-4b9b-a2be-98e81cb21ccf"
InstallerVersion="500" Compressed="yes" Codepage="utf-8" > InstallerVersion="500" Compressed="yes" Codepage="utf-8" >
<MediaTemplate EmbedCab="yes" /> <MediaTemplate EmbedCab="yes" />
<Feature Id="NetbirdFeature" Title="Netbird" Level="1"> <Feature Id="NetbirdFeature" Title="Netbird" Level="1">
@@ -46,29 +48,10 @@
<ComponentRef Id="NetbirdFiles" /> <ComponentRef Id="NetbirdFiles" />
</ComponentGroup> </ComponentGroup>
<Property Id="cmd" Value="cmd.exe"/> <util:CloseApplication Id="CloseNetBird" CloseMessage="no" Target="netbird.exe" RebootPrompt="no" />
<util:CloseApplication Id="CloseNetBirdUI" CloseMessage="no" Target="netbird-ui.exe" RebootPrompt="no" />
<CustomAction Id="KillDaemon"
ExeCommand='/c "taskkill /im netbird.exe"'
Execute="deferred"
Property="cmd"
Impersonate="no"
Return="ignore"
/>
<CustomAction Id="KillUI"
ExeCommand='/c "taskkill /im netbird-ui.exe"'
Execute="deferred"
Property="cmd"
Impersonate="no"
Return="ignore"
/>
<InstallExecuteSequence>
<!-- For Uninstallation -->
<Custom Action="KillDaemon" Before="RemoveFiles" Condition="Installed"/>
<Custom Action="KillUI" After="KillDaemon" Condition="Installed"/>
</InstallExecuteSequence>
<!-- Icons --> <!-- Icons -->
<Icon Id="NetbirdIcon" SourceFile=".\client\ui\assets\netbird.ico" /> <Icon Id="NetbirdIcon" SourceFile=".\client\ui\assets\netbird.ico" />
+794 -1862
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -158,6 +158,7 @@ message UpResponse {}
message StatusRequest{ message StatusRequest{
bool getFullPeerStatus = 1; bool getFullPeerStatus = 1;
bool shouldRunProbes = 2;
} }
message StatusResponse{ message StatusResponse{
+113 -79
View File
@@ -1,4 +1,8 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.3
// source: daemon.proto
package proto package proto
@@ -11,8 +15,31 @@ import (
// This is a compile-time assertion to ensure that this generated file // This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against. // is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later. // Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion7 const _ = grpc.SupportPackageIsVersion9
const (
DaemonService_Login_FullMethodName = "/daemon.DaemonService/Login"
DaemonService_WaitSSOLogin_FullMethodName = "/daemon.DaemonService/WaitSSOLogin"
DaemonService_Up_FullMethodName = "/daemon.DaemonService/Up"
DaemonService_Status_FullMethodName = "/daemon.DaemonService/Status"
DaemonService_Down_FullMethodName = "/daemon.DaemonService/Down"
DaemonService_GetConfig_FullMethodName = "/daemon.DaemonService/GetConfig"
DaemonService_ListNetworks_FullMethodName = "/daemon.DaemonService/ListNetworks"
DaemonService_SelectNetworks_FullMethodName = "/daemon.DaemonService/SelectNetworks"
DaemonService_DeselectNetworks_FullMethodName = "/daemon.DaemonService/DeselectNetworks"
DaemonService_ForwardingRules_FullMethodName = "/daemon.DaemonService/ForwardingRules"
DaemonService_DebugBundle_FullMethodName = "/daemon.DaemonService/DebugBundle"
DaemonService_GetLogLevel_FullMethodName = "/daemon.DaemonService/GetLogLevel"
DaemonService_SetLogLevel_FullMethodName = "/daemon.DaemonService/SetLogLevel"
DaemonService_ListStates_FullMethodName = "/daemon.DaemonService/ListStates"
DaemonService_CleanState_FullMethodName = "/daemon.DaemonService/CleanState"
DaemonService_DeleteState_FullMethodName = "/daemon.DaemonService/DeleteState"
DaemonService_SetNetworkMapPersistence_FullMethodName = "/daemon.DaemonService/SetNetworkMapPersistence"
DaemonService_TracePacket_FullMethodName = "/daemon.DaemonService/TracePacket"
DaemonService_SubscribeEvents_FullMethodName = "/daemon.DaemonService/SubscribeEvents"
DaemonService_GetEvents_FullMethodName = "/daemon.DaemonService/GetEvents"
)
// DaemonServiceClient is the client API for DaemonService service. // DaemonServiceClient is the client API for DaemonService service.
// //
@@ -53,7 +80,7 @@ type DaemonServiceClient interface {
// SetNetworkMapPersistence enables or disables network map persistence // SetNetworkMapPersistence enables or disables network map persistence
SetNetworkMapPersistence(ctx context.Context, in *SetNetworkMapPersistenceRequest, opts ...grpc.CallOption) (*SetNetworkMapPersistenceResponse, error) SetNetworkMapPersistence(ctx context.Context, in *SetNetworkMapPersistenceRequest, opts ...grpc.CallOption) (*SetNetworkMapPersistenceResponse, error)
TracePacket(ctx context.Context, in *TracePacketRequest, opts ...grpc.CallOption) (*TracePacketResponse, error) TracePacket(ctx context.Context, in *TracePacketRequest, opts ...grpc.CallOption) (*TracePacketResponse, error)
SubscribeEvents(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (DaemonService_SubscribeEventsClient, error) SubscribeEvents(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SystemEvent], error)
GetEvents(ctx context.Context, in *GetEventsRequest, opts ...grpc.CallOption) (*GetEventsResponse, error) GetEvents(ctx context.Context, in *GetEventsRequest, opts ...grpc.CallOption) (*GetEventsResponse, error)
} }
@@ -66,8 +93,9 @@ func NewDaemonServiceClient(cc grpc.ClientConnInterface) DaemonServiceClient {
} }
func (c *daemonServiceClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { func (c *daemonServiceClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LoginResponse) out := new(LoginResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/Login", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_Login_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -75,8 +103,9 @@ func (c *daemonServiceClient) Login(ctx context.Context, in *LoginRequest, opts
} }
func (c *daemonServiceClient) WaitSSOLogin(ctx context.Context, in *WaitSSOLoginRequest, opts ...grpc.CallOption) (*WaitSSOLoginResponse, error) { func (c *daemonServiceClient) WaitSSOLogin(ctx context.Context, in *WaitSSOLoginRequest, opts ...grpc.CallOption) (*WaitSSOLoginResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(WaitSSOLoginResponse) out := new(WaitSSOLoginResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/WaitSSOLogin", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_WaitSSOLogin_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -84,8 +113,9 @@ func (c *daemonServiceClient) WaitSSOLogin(ctx context.Context, in *WaitSSOLogin
} }
func (c *daemonServiceClient) Up(ctx context.Context, in *UpRequest, opts ...grpc.CallOption) (*UpResponse, error) { func (c *daemonServiceClient) Up(ctx context.Context, in *UpRequest, opts ...grpc.CallOption) (*UpResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpResponse) out := new(UpResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/Up", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_Up_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -93,8 +123,9 @@ func (c *daemonServiceClient) Up(ctx context.Context, in *UpRequest, opts ...grp
} }
func (c *daemonServiceClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) { func (c *daemonServiceClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusResponse) out := new(StatusResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/Status", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_Status_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -102,8 +133,9 @@ func (c *daemonServiceClient) Status(ctx context.Context, in *StatusRequest, opt
} }
func (c *daemonServiceClient) Down(ctx context.Context, in *DownRequest, opts ...grpc.CallOption) (*DownResponse, error) { func (c *daemonServiceClient) Down(ctx context.Context, in *DownRequest, opts ...grpc.CallOption) (*DownResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DownResponse) out := new(DownResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/Down", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_Down_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -111,8 +143,9 @@ func (c *daemonServiceClient) Down(ctx context.Context, in *DownRequest, opts ..
} }
func (c *daemonServiceClient) GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error) { func (c *daemonServiceClient) GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetConfigResponse) out := new(GetConfigResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/GetConfig", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_GetConfig_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -120,8 +153,9 @@ func (c *daemonServiceClient) GetConfig(ctx context.Context, in *GetConfigReques
} }
func (c *daemonServiceClient) ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) { func (c *daemonServiceClient) ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListNetworksResponse) out := new(ListNetworksResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/ListNetworks", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_ListNetworks_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -129,8 +163,9 @@ func (c *daemonServiceClient) ListNetworks(ctx context.Context, in *ListNetworks
} }
func (c *daemonServiceClient) SelectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) { func (c *daemonServiceClient) SelectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SelectNetworksResponse) out := new(SelectNetworksResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/SelectNetworks", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_SelectNetworks_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -138,8 +173,9 @@ func (c *daemonServiceClient) SelectNetworks(ctx context.Context, in *SelectNetw
} }
func (c *daemonServiceClient) DeselectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) { func (c *daemonServiceClient) DeselectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SelectNetworksResponse) out := new(SelectNetworksResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/DeselectNetworks", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_DeselectNetworks_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -147,8 +183,9 @@ func (c *daemonServiceClient) DeselectNetworks(ctx context.Context, in *SelectNe
} }
func (c *daemonServiceClient) ForwardingRules(ctx context.Context, in *EmptyRequest, opts ...grpc.CallOption) (*ForwardingRulesResponse, error) { func (c *daemonServiceClient) ForwardingRules(ctx context.Context, in *EmptyRequest, opts ...grpc.CallOption) (*ForwardingRulesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ForwardingRulesResponse) out := new(ForwardingRulesResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/ForwardingRules", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_ForwardingRules_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -156,8 +193,9 @@ func (c *daemonServiceClient) ForwardingRules(ctx context.Context, in *EmptyRequ
} }
func (c *daemonServiceClient) DebugBundle(ctx context.Context, in *DebugBundleRequest, opts ...grpc.CallOption) (*DebugBundleResponse, error) { func (c *daemonServiceClient) DebugBundle(ctx context.Context, in *DebugBundleRequest, opts ...grpc.CallOption) (*DebugBundleResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DebugBundleResponse) out := new(DebugBundleResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/DebugBundle", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_DebugBundle_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -165,8 +203,9 @@ func (c *daemonServiceClient) DebugBundle(ctx context.Context, in *DebugBundleRe
} }
func (c *daemonServiceClient) GetLogLevel(ctx context.Context, in *GetLogLevelRequest, opts ...grpc.CallOption) (*GetLogLevelResponse, error) { func (c *daemonServiceClient) GetLogLevel(ctx context.Context, in *GetLogLevelRequest, opts ...grpc.CallOption) (*GetLogLevelResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetLogLevelResponse) out := new(GetLogLevelResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/GetLogLevel", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_GetLogLevel_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -174,8 +213,9 @@ func (c *daemonServiceClient) GetLogLevel(ctx context.Context, in *GetLogLevelRe
} }
func (c *daemonServiceClient) SetLogLevel(ctx context.Context, in *SetLogLevelRequest, opts ...grpc.CallOption) (*SetLogLevelResponse, error) { func (c *daemonServiceClient) SetLogLevel(ctx context.Context, in *SetLogLevelRequest, opts ...grpc.CallOption) (*SetLogLevelResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetLogLevelResponse) out := new(SetLogLevelResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/SetLogLevel", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_SetLogLevel_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -183,8 +223,9 @@ func (c *daemonServiceClient) SetLogLevel(ctx context.Context, in *SetLogLevelRe
} }
func (c *daemonServiceClient) ListStates(ctx context.Context, in *ListStatesRequest, opts ...grpc.CallOption) (*ListStatesResponse, error) { func (c *daemonServiceClient) ListStates(ctx context.Context, in *ListStatesRequest, opts ...grpc.CallOption) (*ListStatesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListStatesResponse) out := new(ListStatesResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/ListStates", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_ListStates_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -192,8 +233,9 @@ func (c *daemonServiceClient) ListStates(ctx context.Context, in *ListStatesRequ
} }
func (c *daemonServiceClient) CleanState(ctx context.Context, in *CleanStateRequest, opts ...grpc.CallOption) (*CleanStateResponse, error) { func (c *daemonServiceClient) CleanState(ctx context.Context, in *CleanStateRequest, opts ...grpc.CallOption) (*CleanStateResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CleanStateResponse) out := new(CleanStateResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/CleanState", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_CleanState_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -201,8 +243,9 @@ func (c *daemonServiceClient) CleanState(ctx context.Context, in *CleanStateRequ
} }
func (c *daemonServiceClient) DeleteState(ctx context.Context, in *DeleteStateRequest, opts ...grpc.CallOption) (*DeleteStateResponse, error) { func (c *daemonServiceClient) DeleteState(ctx context.Context, in *DeleteStateRequest, opts ...grpc.CallOption) (*DeleteStateResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteStateResponse) out := new(DeleteStateResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/DeleteState", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_DeleteState_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -210,8 +253,9 @@ func (c *daemonServiceClient) DeleteState(ctx context.Context, in *DeleteStateRe
} }
func (c *daemonServiceClient) SetNetworkMapPersistence(ctx context.Context, in *SetNetworkMapPersistenceRequest, opts ...grpc.CallOption) (*SetNetworkMapPersistenceResponse, error) { func (c *daemonServiceClient) SetNetworkMapPersistence(ctx context.Context, in *SetNetworkMapPersistenceRequest, opts ...grpc.CallOption) (*SetNetworkMapPersistenceResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetNetworkMapPersistenceResponse) out := new(SetNetworkMapPersistenceResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/SetNetworkMapPersistence", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_SetNetworkMapPersistence_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -219,20 +263,22 @@ func (c *daemonServiceClient) SetNetworkMapPersistence(ctx context.Context, in *
} }
func (c *daemonServiceClient) TracePacket(ctx context.Context, in *TracePacketRequest, opts ...grpc.CallOption) (*TracePacketResponse, error) { func (c *daemonServiceClient) TracePacket(ctx context.Context, in *TracePacketRequest, opts ...grpc.CallOption) (*TracePacketResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TracePacketResponse) out := new(TracePacketResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/TracePacket", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_TracePacket_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil
} }
func (c *daemonServiceClient) SubscribeEvents(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (DaemonService_SubscribeEventsClient, error) { func (c *daemonServiceClient) SubscribeEvents(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SystemEvent], error) {
stream, err := c.cc.NewStream(ctx, &DaemonService_ServiceDesc.Streams[0], "/daemon.DaemonService/SubscribeEvents", opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &DaemonService_ServiceDesc.Streams[0], DaemonService_SubscribeEvents_FullMethodName, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
x := &daemonServiceSubscribeEventsClient{stream} x := &grpc.GenericClientStream[SubscribeRequest, SystemEvent]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil { if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err return nil, err
} }
@@ -242,26 +288,13 @@ func (c *daemonServiceClient) SubscribeEvents(ctx context.Context, in *Subscribe
return x, nil return x, nil
} }
type DaemonService_SubscribeEventsClient interface { // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
Recv() (*SystemEvent, error) type DaemonService_SubscribeEventsClient = grpc.ServerStreamingClient[SystemEvent]
grpc.ClientStream
}
type daemonServiceSubscribeEventsClient struct {
grpc.ClientStream
}
func (x *daemonServiceSubscribeEventsClient) Recv() (*SystemEvent, error) {
m := new(SystemEvent)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *daemonServiceClient) GetEvents(ctx context.Context, in *GetEventsRequest, opts ...grpc.CallOption) (*GetEventsResponse, error) { func (c *daemonServiceClient) GetEvents(ctx context.Context, in *GetEventsRequest, opts ...grpc.CallOption) (*GetEventsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetEventsResponse) out := new(GetEventsResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/GetEvents", in, out, opts...) err := c.cc.Invoke(ctx, DaemonService_GetEvents_FullMethodName, in, out, cOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -270,7 +303,7 @@ func (c *daemonServiceClient) GetEvents(ctx context.Context, in *GetEventsReques
// DaemonServiceServer is the server API for DaemonService service. // DaemonServiceServer is the server API for DaemonService service.
// All implementations must embed UnimplementedDaemonServiceServer // All implementations must embed UnimplementedDaemonServiceServer
// for forward compatibility // for forward compatibility.
type DaemonServiceServer interface { type DaemonServiceServer interface {
// Login uses setup key to prepare configuration for the daemon. // Login uses setup key to prepare configuration for the daemon.
Login(context.Context, *LoginRequest) (*LoginResponse, error) Login(context.Context, *LoginRequest) (*LoginResponse, error)
@@ -307,14 +340,17 @@ type DaemonServiceServer interface {
// SetNetworkMapPersistence enables or disables network map persistence // SetNetworkMapPersistence enables or disables network map persistence
SetNetworkMapPersistence(context.Context, *SetNetworkMapPersistenceRequest) (*SetNetworkMapPersistenceResponse, error) SetNetworkMapPersistence(context.Context, *SetNetworkMapPersistenceRequest) (*SetNetworkMapPersistenceResponse, error)
TracePacket(context.Context, *TracePacketRequest) (*TracePacketResponse, error) TracePacket(context.Context, *TracePacketRequest) (*TracePacketResponse, error)
SubscribeEvents(*SubscribeRequest, DaemonService_SubscribeEventsServer) error SubscribeEvents(*SubscribeRequest, grpc.ServerStreamingServer[SystemEvent]) error
GetEvents(context.Context, *GetEventsRequest) (*GetEventsResponse, error) GetEvents(context.Context, *GetEventsRequest) (*GetEventsResponse, error)
mustEmbedUnimplementedDaemonServiceServer() mustEmbedUnimplementedDaemonServiceServer()
} }
// UnimplementedDaemonServiceServer must be embedded to have forward compatible implementations. // UnimplementedDaemonServiceServer must be embedded to have
type UnimplementedDaemonServiceServer struct { // forward compatible implementations.
} //
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedDaemonServiceServer struct{}
func (UnimplementedDaemonServiceServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) { func (UnimplementedDaemonServiceServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
@@ -370,13 +406,14 @@ func (UnimplementedDaemonServiceServer) SetNetworkMapPersistence(context.Context
func (UnimplementedDaemonServiceServer) TracePacket(context.Context, *TracePacketRequest) (*TracePacketResponse, error) { func (UnimplementedDaemonServiceServer) TracePacket(context.Context, *TracePacketRequest) (*TracePacketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method TracePacket not implemented") return nil, status.Errorf(codes.Unimplemented, "method TracePacket not implemented")
} }
func (UnimplementedDaemonServiceServer) SubscribeEvents(*SubscribeRequest, DaemonService_SubscribeEventsServer) error { func (UnimplementedDaemonServiceServer) SubscribeEvents(*SubscribeRequest, grpc.ServerStreamingServer[SystemEvent]) error {
return status.Errorf(codes.Unimplemented, "method SubscribeEvents not implemented") return status.Errorf(codes.Unimplemented, "method SubscribeEvents not implemented")
} }
func (UnimplementedDaemonServiceServer) GetEvents(context.Context, *GetEventsRequest) (*GetEventsResponse, error) { func (UnimplementedDaemonServiceServer) GetEvents(context.Context, *GetEventsRequest) (*GetEventsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetEvents not implemented") return nil, status.Errorf(codes.Unimplemented, "method GetEvents not implemented")
} }
func (UnimplementedDaemonServiceServer) mustEmbedUnimplementedDaemonServiceServer() {} func (UnimplementedDaemonServiceServer) mustEmbedUnimplementedDaemonServiceServer() {}
func (UnimplementedDaemonServiceServer) testEmbeddedByValue() {}
// UnsafeDaemonServiceServer may be embedded to opt out of forward compatibility for this service. // UnsafeDaemonServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to DaemonServiceServer will // Use of this interface is not recommended, as added methods to DaemonServiceServer will
@@ -386,6 +423,13 @@ type UnsafeDaemonServiceServer interface {
} }
func RegisterDaemonServiceServer(s grpc.ServiceRegistrar, srv DaemonServiceServer) { func RegisterDaemonServiceServer(s grpc.ServiceRegistrar, srv DaemonServiceServer) {
// If the following call pancis, it indicates UnimplementedDaemonServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&DaemonService_ServiceDesc, srv) s.RegisterService(&DaemonService_ServiceDesc, srv)
} }
@@ -399,7 +443,7 @@ func _DaemonService_Login_Handler(srv interface{}, ctx context.Context, dec func
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/Login", FullMethod: DaemonService_Login_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).Login(ctx, req.(*LoginRequest)) return srv.(DaemonServiceServer).Login(ctx, req.(*LoginRequest))
@@ -417,7 +461,7 @@ func _DaemonService_WaitSSOLogin_Handler(srv interface{}, ctx context.Context, d
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/WaitSSOLogin", FullMethod: DaemonService_WaitSSOLogin_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).WaitSSOLogin(ctx, req.(*WaitSSOLoginRequest)) return srv.(DaemonServiceServer).WaitSSOLogin(ctx, req.(*WaitSSOLoginRequest))
@@ -435,7 +479,7 @@ func _DaemonService_Up_Handler(srv interface{}, ctx context.Context, dec func(in
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/Up", FullMethod: DaemonService_Up_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).Up(ctx, req.(*UpRequest)) return srv.(DaemonServiceServer).Up(ctx, req.(*UpRequest))
@@ -453,7 +497,7 @@ func _DaemonService_Status_Handler(srv interface{}, ctx context.Context, dec fun
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/Status", FullMethod: DaemonService_Status_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).Status(ctx, req.(*StatusRequest)) return srv.(DaemonServiceServer).Status(ctx, req.(*StatusRequest))
@@ -471,7 +515,7 @@ func _DaemonService_Down_Handler(srv interface{}, ctx context.Context, dec func(
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/Down", FullMethod: DaemonService_Down_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).Down(ctx, req.(*DownRequest)) return srv.(DaemonServiceServer).Down(ctx, req.(*DownRequest))
@@ -489,7 +533,7 @@ func _DaemonService_GetConfig_Handler(srv interface{}, ctx context.Context, dec
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/GetConfig", FullMethod: DaemonService_GetConfig_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).GetConfig(ctx, req.(*GetConfigRequest)) return srv.(DaemonServiceServer).GetConfig(ctx, req.(*GetConfigRequest))
@@ -507,7 +551,7 @@ func _DaemonService_ListNetworks_Handler(srv interface{}, ctx context.Context, d
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/ListNetworks", FullMethod: DaemonService_ListNetworks_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).ListNetworks(ctx, req.(*ListNetworksRequest)) return srv.(DaemonServiceServer).ListNetworks(ctx, req.(*ListNetworksRequest))
@@ -525,7 +569,7 @@ func _DaemonService_SelectNetworks_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/SelectNetworks", FullMethod: DaemonService_SelectNetworks_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).SelectNetworks(ctx, req.(*SelectNetworksRequest)) return srv.(DaemonServiceServer).SelectNetworks(ctx, req.(*SelectNetworksRequest))
@@ -543,7 +587,7 @@ func _DaemonService_DeselectNetworks_Handler(srv interface{}, ctx context.Contex
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/DeselectNetworks", FullMethod: DaemonService_DeselectNetworks_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).DeselectNetworks(ctx, req.(*SelectNetworksRequest)) return srv.(DaemonServiceServer).DeselectNetworks(ctx, req.(*SelectNetworksRequest))
@@ -561,7 +605,7 @@ func _DaemonService_ForwardingRules_Handler(srv interface{}, ctx context.Context
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/ForwardingRules", FullMethod: DaemonService_ForwardingRules_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).ForwardingRules(ctx, req.(*EmptyRequest)) return srv.(DaemonServiceServer).ForwardingRules(ctx, req.(*EmptyRequest))
@@ -579,7 +623,7 @@ func _DaemonService_DebugBundle_Handler(srv interface{}, ctx context.Context, de
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/DebugBundle", FullMethod: DaemonService_DebugBundle_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).DebugBundle(ctx, req.(*DebugBundleRequest)) return srv.(DaemonServiceServer).DebugBundle(ctx, req.(*DebugBundleRequest))
@@ -597,7 +641,7 @@ func _DaemonService_GetLogLevel_Handler(srv interface{}, ctx context.Context, de
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/GetLogLevel", FullMethod: DaemonService_GetLogLevel_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).GetLogLevel(ctx, req.(*GetLogLevelRequest)) return srv.(DaemonServiceServer).GetLogLevel(ctx, req.(*GetLogLevelRequest))
@@ -615,7 +659,7 @@ func _DaemonService_SetLogLevel_Handler(srv interface{}, ctx context.Context, de
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/SetLogLevel", FullMethod: DaemonService_SetLogLevel_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).SetLogLevel(ctx, req.(*SetLogLevelRequest)) return srv.(DaemonServiceServer).SetLogLevel(ctx, req.(*SetLogLevelRequest))
@@ -633,7 +677,7 @@ func _DaemonService_ListStates_Handler(srv interface{}, ctx context.Context, dec
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/ListStates", FullMethod: DaemonService_ListStates_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).ListStates(ctx, req.(*ListStatesRequest)) return srv.(DaemonServiceServer).ListStates(ctx, req.(*ListStatesRequest))
@@ -651,7 +695,7 @@ func _DaemonService_CleanState_Handler(srv interface{}, ctx context.Context, dec
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/CleanState", FullMethod: DaemonService_CleanState_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).CleanState(ctx, req.(*CleanStateRequest)) return srv.(DaemonServiceServer).CleanState(ctx, req.(*CleanStateRequest))
@@ -669,7 +713,7 @@ func _DaemonService_DeleteState_Handler(srv interface{}, ctx context.Context, de
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/DeleteState", FullMethod: DaemonService_DeleteState_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).DeleteState(ctx, req.(*DeleteStateRequest)) return srv.(DaemonServiceServer).DeleteState(ctx, req.(*DeleteStateRequest))
@@ -687,7 +731,7 @@ func _DaemonService_SetNetworkMapPersistence_Handler(srv interface{}, ctx contex
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/SetNetworkMapPersistence", FullMethod: DaemonService_SetNetworkMapPersistence_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).SetNetworkMapPersistence(ctx, req.(*SetNetworkMapPersistenceRequest)) return srv.(DaemonServiceServer).SetNetworkMapPersistence(ctx, req.(*SetNetworkMapPersistenceRequest))
@@ -705,7 +749,7 @@ func _DaemonService_TracePacket_Handler(srv interface{}, ctx context.Context, de
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/TracePacket", FullMethod: DaemonService_TracePacket_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).TracePacket(ctx, req.(*TracePacketRequest)) return srv.(DaemonServiceServer).TracePacket(ctx, req.(*TracePacketRequest))
@@ -718,21 +762,11 @@ func _DaemonService_SubscribeEvents_Handler(srv interface{}, stream grpc.ServerS
if err := stream.RecvMsg(m); err != nil { if err := stream.RecvMsg(m); err != nil {
return err return err
} }
return srv.(DaemonServiceServer).SubscribeEvents(m, &daemonServiceSubscribeEventsServer{stream}) return srv.(DaemonServiceServer).SubscribeEvents(m, &grpc.GenericServerStream[SubscribeRequest, SystemEvent]{ServerStream: stream})
} }
type DaemonService_SubscribeEventsServer interface { // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
Send(*SystemEvent) error type DaemonService_SubscribeEventsServer = grpc.ServerStreamingServer[SystemEvent]
grpc.ServerStream
}
type daemonServiceSubscribeEventsServer struct {
grpc.ServerStream
}
func (x *daemonServiceSubscribeEventsServer) Send(m *SystemEvent) error {
return x.ServerStream.SendMsg(m)
}
func _DaemonService_GetEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _DaemonService_GetEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetEventsRequest) in := new(GetEventsRequest)
@@ -744,7 +778,7 @@ func _DaemonService_GetEvents_Handler(srv interface{}, ctx context.Context, dec
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/daemon.DaemonService/GetEvents", FullMethod: DaemonService_GetEvents_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).GetEvents(ctx, req.(*GetEventsRequest)) return srv.(DaemonServiceServer).GetEvents(ctx, req.(*GetEventsRequest))
+3 -1
View File
@@ -707,7 +707,9 @@ func (s *Server) Status(
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive) s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
if msg.GetFullPeerStatus { if msg.GetFullPeerStatus {
s.runProbes() if msg.ShouldRunProbes {
s.runProbes()
}
fullStatus := s.statusRecorder.GetFullStatus() fullStatus := s.statusRecorder.GetFullStatus()
pbFullStatus := toProtoFullStatus(fullStatus) pbFullStatus := toProtoFullStatus(fullStatus)
+1 -1
View File
@@ -206,7 +206,7 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
settingsMockManager := settings.NewMockManager(ctrl) settingsMockManager := settings.NewMockManager(ctrl)
permissionsManagerMock := permissions.NewMockManager(ctrl) permissionsManagerMock := permissions.NewMockManager(ctrl)
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock) accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
+30 -18
View File
@@ -280,7 +280,7 @@ func newServiceClient(addr string, logFile string, a fyne.App, showSettings bool
showAdvancedSettings: showSettings, showAdvancedSettings: showSettings,
showNetworks: showNetworks, showNetworks: showNetworks,
update: version.NewUpdate(), update: version.NewUpdate("nb/client-ui"),
} }
s.eventHandler = newEventHandler(s) s.eventHandler = newEventHandler(s)
@@ -879,7 +879,7 @@ func (s *serviceClient) onUpdateAvailable() {
func (s *serviceClient) onSessionExpire() { func (s *serviceClient) onSessionExpire() {
s.sendNotification = true s.sendNotification = true
if s.sendNotification { if s.sendNotification {
s.eventHandler.runSelfCommand("login-url", "true") go s.eventHandler.runSelfCommand(s.ctx, "login-url", "true")
s.sendNotification = false s.sendNotification = false
} }
} }
@@ -992,21 +992,6 @@ func (s *serviceClient) restartClient(loginRequest *proto.LoginRequest) error {
// showLoginURL creates a borderless window styled like a pop-up in the top-right corner using s.wLoginURL. // showLoginURL creates a borderless window styled like a pop-up in the top-right corner using s.wLoginURL.
func (s *serviceClient) showLoginURL() { func (s *serviceClient) showLoginURL() {
resp, err := s.login(false)
if err != nil {
log.Errorf("failed to fetch login URL: %v", err)
return
}
verificationURL := resp.VerificationURIComplete
if verificationURL == "" {
verificationURL = resp.VerificationURI
}
if verificationURL == "" {
log.Error("no verification URL provided in the login response")
return
}
resIcon := fyne.NewStaticResource("netbird.png", iconAbout) resIcon := fyne.NewStaticResource("netbird.png", iconAbout)
if s.wLoginURL == nil { if s.wLoginURL == nil {
@@ -1025,6 +1010,21 @@ func (s *serviceClient) showLoginURL() {
return return
} }
resp, err := s.login(false)
if err != nil {
log.Errorf("failed to fetch login URL: %v", err)
return
}
verificationURL := resp.VerificationURIComplete
if verificationURL == "" {
verificationURL = resp.VerificationURI
}
if verificationURL == "" {
log.Error("no verification URL provided in the login response")
return
}
if err := openURL(verificationURL); err != nil { if err := openURL(verificationURL); err != nil {
log.Errorf("failed to open login URL: %v", err) log.Errorf("failed to open login URL: %v", err)
return return
@@ -1038,7 +1038,19 @@ func (s *serviceClient) showLoginURL() {
} }
label.SetText("Re-authentication successful.\nReconnecting") label.SetText("Re-authentication successful.\nReconnecting")
time.Sleep(300 * time.Millisecond) status, err := conn.Status(s.ctx, &proto.StatusRequest{})
if err != nil {
log.Errorf("get service status: %v", err)
return
}
if status.Status == string(internal.StatusConnected) {
label.SetText("Already connected.\nClosing this window.")
time.Sleep(2 * time.Second)
s.wLoginURL.Close()
return
}
_, err = conn.Up(s.ctx, &proto.UpRequest{}) _, err = conn.Up(s.ctx, &proto.UpRequest{})
if err != nil { if err != nil {
label.SetText("Reconnecting failed, please create \na debug bundle in the settings and contact support.") label.SetText("Reconnecting failed, please create \na debug bundle in the settings and contact support.")
+5 -5
View File
@@ -122,7 +122,7 @@ func (h *eventHandler) handleAdvancedSettingsClick() {
go func() { go func() {
defer h.client.mAdvancedSettings.Enable() defer h.client.mAdvancedSettings.Enable()
defer h.client.getSrvConfig() defer h.client.getSrvConfig()
h.runSelfCommand("settings", "true") h.runSelfCommand(h.client.ctx, "settings", "true")
}() }()
} }
@@ -130,7 +130,7 @@ func (h *eventHandler) handleCreateDebugBundleClick() {
h.client.mCreateDebugBundle.Disable() h.client.mCreateDebugBundle.Disable()
go func() { go func() {
defer h.client.mCreateDebugBundle.Enable() defer h.client.mCreateDebugBundle.Enable()
h.runSelfCommand("debug", "true") h.runSelfCommand(h.client.ctx, "debug", "true")
}() }()
} }
@@ -154,7 +154,7 @@ func (h *eventHandler) handleNetworksClick() {
h.client.mNetworks.Disable() h.client.mNetworks.Disable()
go func() { go func() {
defer h.client.mNetworks.Enable() defer h.client.mNetworks.Enable()
h.runSelfCommand("networks", "true") h.runSelfCommand(h.client.ctx, "networks", "true")
}() }()
} }
@@ -172,14 +172,14 @@ func (h *eventHandler) updateConfigWithErr() {
} }
} }
func (h *eventHandler) runSelfCommand(command, arg string) { func (h *eventHandler) runSelfCommand(ctx context.Context, command, arg string) {
proc, err := os.Executable() proc, err := os.Executable()
if err != nil { if err != nil {
log.Errorf("error getting executable path: %v", err) log.Errorf("error getting executable path: %v", err)
return return
} }
cmd := exec.Command(proc, cmd := exec.CommandContext(ctx, proc,
fmt.Sprintf("--%s=%s", command, arg), fmt.Sprintf("--%s=%s", command, arg),
fmt.Sprintf("--daemon-addr=%s", h.client.addr), fmt.Sprintf("--daemon-addr=%s", h.client.addr),
) )
+3 -1
View File
@@ -15,6 +15,7 @@ NETBIRD_MGMT_API_CERT_KEY_FILE="/etc/letsencrypt/live/$NETBIRD_LETSENCRYPT_DOMAI
NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN=$NETBIRD_DOMAIN NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN=$NETBIRD_DOMAIN
NETBIRD_MGMT_DNS_DOMAIN=${NETBIRD_MGMT_DNS_DOMAIN:-netbird.selfhosted} NETBIRD_MGMT_DNS_DOMAIN=${NETBIRD_MGMT_DNS_DOMAIN:-netbird.selfhosted}
NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=${NETBIRD_MGMT_IDP_SIGNKEY_REFRESH:-false} NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=${NETBIRD_MGMT_IDP_SIGNKEY_REFRESH:-false}
NETBIRD_MGMT_DISABLE_DEFAULT_POLICY=${NETBIRD_MGMT_DISABLE_DEFAULT_POLICY:-false}
# Signal # Signal
NETBIRD_SIGNAL_PROTOCOL="http" NETBIRD_SIGNAL_PROTOCOL="http"
@@ -60,7 +61,7 @@ NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS=${NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS:-"53000"} NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS=${NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS:-"53000"}
NETBIRD_AUTH_PKCE_USE_ID_TOKEN=${NETBIRD_AUTH_PKCE_USE_ID_TOKEN:-false} NETBIRD_AUTH_PKCE_USE_ID_TOKEN=${NETBIRD_AUTH_PKCE_USE_ID_TOKEN:-false}
NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN=${NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN:-false} NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN=${NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN:-false}
NETBIRD_AUTH_PKCE_LOGIN_FLAG=${NETBIRD_AUTH_PKCE_LOGIN_FLAG:-1} NETBIRD_AUTH_PKCE_LOGIN_FLAG=${NETBIRD_AUTH_PKCE_LOGIN_FLAG:-0}
NETBIRD_AUTH_PKCE_AUDIENCE=$NETBIRD_AUTH_AUDIENCE NETBIRD_AUTH_PKCE_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
# Dashboard # Dashboard
@@ -139,3 +140,4 @@ export NETBIRD_RELAY_PORT
export NETBIRD_RELAY_ENDPOINT export NETBIRD_RELAY_ENDPOINT
export NETBIRD_RELAY_AUTH_SECRET export NETBIRD_RELAY_AUTH_SECRET
export NETBIRD_RELAY_TAG export NETBIRD_RELAY_TAG
export NETBIRD_MGMT_DISABLE_DEFAULT_POLICY
@@ -791,7 +791,6 @@ services:
- '443:443' - '443:443'
- '443:443/udp' - '443:443/udp'
- '80:80' - '80:80'
- '8080:8080'
volumes: volumes:
- netbird_caddy_data:/data - netbird_caddy_data:/data
- ./Caddyfile:/etc/caddy/Caddyfile - ./Caddyfile:/etc/caddy/Caddyfile
@@ -38,6 +38,7 @@
"0.0.0.0/0" "0.0.0.0/0"
] ]
}, },
"DisableDefaultPolicy": $NETBIRD_MGMT_DISABLE_DEFAULT_POLICY,
"Datadir": "", "Datadir": "",
"DataStoreEncryptionKey": "$NETBIRD_DATASTORE_ENC_KEY", "DataStoreEncryptionKey": "$NETBIRD_DATASTORE_ENC_KEY",
"StoreConfig": { "StoreConfig": {
+2 -1
View File
@@ -92,7 +92,8 @@ NETBIRD_LETSENCRYPT_EMAIL=""
NETBIRD_DISABLE_ANONYMOUS_METRICS=false NETBIRD_DISABLE_ANONYMOUS_METRICS=false
# DNS DOMAIN configures the domain name used for peer resolution. By default it is netbird.selfhosted # DNS DOMAIN configures the domain name used for peer resolution. By default it is netbird.selfhosted
NETBIRD_MGMT_DNS_DOMAIN=netbird.selfhosted NETBIRD_MGMT_DNS_DOMAIN=netbird.selfhosted
# Disable default all-to-all policy for new accounts
NETBIRD_MGMT_DISABLE_DEFAULT_POLICY=false
# ------------------------------------------- # -------------------------------------------
# Relay settings # Relay settings
# ------------------------------------------- # -------------------------------------------
+1
View File
@@ -29,3 +29,4 @@ NETBIRD_TURN_EXTERNAL_IP=1.2.3.4
NETBIRD_RELAY_PORT=33445 NETBIRD_RELAY_PORT=33445
NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN=true NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN=true
NETBIRD_AUTH_PKCE_LOGIN_FLAG=0 NETBIRD_AUTH_PKCE_LOGIN_FLAG=0
NETBIRD_MGMT_DISABLE_DEFAULT_POLICY=$CI_NETBIRD_MGMT_DISABLE_DEFAULT_POLICY
+1 -1
View File
@@ -100,7 +100,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
Return(true, nil). Return(true, nil).
AnyTimes() AnyTimes()
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock) accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+8 -1
View File
@@ -215,7 +215,7 @@ var (
peersManager := peers.NewManager(store, permissionsManager) peersManager := peers.NewManager(store, permissionsManager)
proxyController := integrations.NewController(store) proxyController := integrations.NewController(store)
accountManager, err := server.BuildManager(ctx, store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain, accountManager, err := server.BuildManager(ctx, store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain,
dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerValidator, appMetrics, proxyController, settingsManager, permissionsManager) dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerValidator, appMetrics, proxyController, settingsManager, permissionsManager, config.DisableDefaultPolicy)
if err != nil { if err != nil {
return fmt.Errorf("failed to build default manager: %v", err) return fmt.Errorf("failed to build default manager: %v", err)
} }
@@ -357,6 +357,13 @@ var (
log.WithContext(ctx).Infof("running HTTP server and gRPC server on the same port: %s", listener.Addr().String()) log.WithContext(ctx).Infof("running HTTP server and gRPC server on the same port: %s", listener.Addr().String())
serveGRPCWithHTTP(ctx, listener, rootHandler, tlsEnabled) serveGRPCWithHTTP(ctx, listener, rootHandler, tlsEnabled)
update := version.NewUpdate("nb/management")
update.SetDaemonVersion(version.NetbirdVersion())
update.SetOnUpdateListener(func() {
log.WithContext(ctx).Infof("your management version, \"%s\", is outdated, a new management version is available. Learn more here: https://github.com/netbirdio/netbird/releases", version.NetbirdVersion())
})
defer update.StopWatch()
SetupCloseHandler() SetupCloseHandler()
<-stopCh <-stopCh
+47 -34
View File
@@ -102,6 +102,8 @@ type DefaultAccountManager struct {
accountUpdateLocks sync.Map accountUpdateLocks sync.Map
updateAccountPeersBufferInterval atomic.Int64 updateAccountPeersBufferInterval atomic.Int64
disableDefaultPolicy bool
} }
// getJWTGroupsChanges calculates the changes needed to sync a user's JWT groups. // getJWTGroupsChanges calculates the changes needed to sync a user's JWT groups.
@@ -170,6 +172,7 @@ func BuildManager(
proxyController port_forwarding.Controller, proxyController port_forwarding.Controller,
settingsManager settings.Manager, settingsManager settings.Manager,
permissionsManager permissions.Manager, permissionsManager permissions.Manager,
disableDefaultPolicy bool,
) (*DefaultAccountManager, error) { ) (*DefaultAccountManager, error) {
start := time.Now() start := time.Now()
defer func() { defer func() {
@@ -195,6 +198,7 @@ func BuildManager(
proxyController: proxyController, proxyController: proxyController,
settingsManager: settingsManager, settingsManager: settingsManager,
permissionsManager: permissionsManager, permissionsManager: permissionsManager,
disableDefaultPolicy: disableDefaultPolicy,
} }
am.startWarmup(ctx) am.startWarmup(ctx)
@@ -543,7 +547,7 @@ func (am *DefaultAccountManager) newAccount(ctx context.Context, userID, domain
log.WithContext(ctx).Warnf("an account with ID already exists, retrying...") log.WithContext(ctx).Warnf("an account with ID already exists, retrying...")
continue continue
case statusErr.Type() == status.NotFound: case statusErr.Type() == status.NotFound:
newAccount := newAccountWithId(ctx, accountId, userID, domain) newAccount := newAccountWithId(ctx, accountId, userID, domain, am.disableDefaultPolicy)
am.StoreEvent(ctx, userID, newAccount.Id, accountId, activity.AccountCreated, nil) am.StoreEvent(ctx, userID, newAccount.Id, accountId, activity.AccountCreated, nil)
return newAccount, nil return newAccount, nil
default: default:
@@ -1688,7 +1692,7 @@ func (am *DefaultAccountManager) GetAccountSettings(ctx context.Context, account
} }
// newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id // newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id
func newAccountWithId(ctx context.Context, accountID, userID, domain string) *types.Account { func newAccountWithId(ctx context.Context, accountID, userID, domain string, disableDefaultPolicy bool) *types.Account {
log.WithContext(ctx).Debugf("creating new account") log.WithContext(ctx).Debugf("creating new account")
network := types.NewNetwork() network := types.NewNetwork()
@@ -1731,7 +1735,7 @@ func newAccountWithId(ctx context.Context, accountID, userID, domain string) *ty
}, },
} }
if err := acc.AddAllGroup(); err != nil { if err := acc.AddAllGroup(disableDefaultPolicy); err != nil {
log.WithContext(ctx).Errorf("error adding all group to account %s: %v", acc.Id, err) log.WithContext(ctx).Errorf("error adding all group to account %s: %v", acc.Id, err)
} }
return acc return acc
@@ -1833,7 +1837,7 @@ func (am *DefaultAccountManager) GetOrCreateAccountByPrivateDomain(ctx context.C
}, },
} }
if err := newAccount.AddAllGroup(); err != nil { if err := newAccount.AddAllGroup(am.disableDefaultPolicy); err != nil {
return nil, false, status.Errorf(status.Internal, "failed to add all group to new account by private domain") return nil, false, status.Errorf(status.Internal, "failed to add all group to new account by private domain")
} }
@@ -1853,40 +1857,49 @@ func (am *DefaultAccountManager) GetOrCreateAccountByPrivateDomain(ctx context.C
} }
func (am *DefaultAccountManager) UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error) { func (am *DefaultAccountManager) UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error) {
account, err := am.Store.GetAccount(ctx, accountId) var account *types.Account
err := am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
var err error
account, err = transaction.GetAccount(ctx, accountId)
if err != nil {
return err
}
if account.IsDomainPrimaryAccount {
return nil
}
existingPrimaryAccountID, err := transaction.GetAccountIDByPrivateDomain(ctx, store.LockingStrengthShare, account.Domain)
// error is not a not found error
if handleNotFound(err) != nil {
return err
}
// a primary account already exists for this private domain
if err == nil {
log.WithContext(ctx).WithFields(log.Fields{
"accountId": accountId,
"existingAccountId": existingPrimaryAccountID,
}).Errorf("cannot update account to primary, another account already exists as primary for the same domain")
return status.Errorf(status.Internal, "cannot update account to primary")
}
account.IsDomainPrimaryAccount = true
if err := transaction.SaveAccount(ctx, account); err != nil {
log.WithContext(ctx).WithFields(log.Fields{
"accountId": accountId,
}).Errorf("failed to update account to primary: %v", err)
return status.Errorf(status.Internal, "failed to update account to primary")
}
return nil
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
if account.IsDomainPrimaryAccount {
return account, nil
}
existingPrimaryAccountID, err := am.Store.GetAccountIDByPrivateDomain(ctx, store.LockingStrengthShare, account.Domain)
// error is not a not found error
if handleNotFound(err) != nil {
return nil, err
}
// a primary account already exists for this private domain
if err == nil {
log.WithContext(ctx).WithFields(log.Fields{
"accountId": accountId,
"existingAccountId": existingPrimaryAccountID,
}).Errorf("cannot update account to primary, another account already exists as primary for the same domain")
return nil, status.Errorf(status.Internal, "cannot update account to primary")
}
account.IsDomainPrimaryAccount = true
if err := am.Store.SaveAccount(ctx, account); err != nil {
log.WithContext(ctx).WithFields(log.Fields{
"accountId": accountId,
}).Errorf("failed to update account to primary: %v", err)
return nil, status.Errorf(status.Internal, "failed to update account to primary")
}
return account, nil return account, nil
} }
+13 -5
View File
@@ -373,7 +373,7 @@ func TestAccount_GetPeerNetworkMap(t *testing.T) {
} }
for _, testCase := range tt { for _, testCase := range tt {
account := newAccountWithId(context.Background(), "account-1", userID, "netbird.io") account := newAccountWithId(context.Background(), "account-1", userID, "netbird.io", false)
account.UpdateSettings(&testCase.accountSettings) account.UpdateSettings(&testCase.accountSettings)
account.Network = network account.Network = network
account.Peers = testCase.peers account.Peers = testCase.peers
@@ -398,7 +398,7 @@ func TestNewAccount(t *testing.T) {
domain := "netbird.io" domain := "netbird.io"
userId := "account_creator" userId := "account_creator"
accountID := "account_id" accountID := "account_id"
account := newAccountWithId(context.Background(), accountID, userId, domain) account := newAccountWithId(context.Background(), accountID, userId, domain, false)
verifyNewAccountHasDefaultFields(t, account, userId, domain, []string{userId}) verifyNewAccountHasDefaultFields(t, account, userId, domain, []string{userId})
} }
@@ -640,7 +640,7 @@ func TestDefaultAccountManager_GetAccountIDFromToken(t *testing.T) {
func TestDefaultAccountManager_SyncUserJWTGroups(t *testing.T) { func TestDefaultAccountManager_SyncUserJWTGroups(t *testing.T) {
userId := "user-id" userId := "user-id"
domain := "test.domain" domain := "test.domain"
_ = newAccountWithId(context.Background(), "", userId, domain) _ = newAccountWithId(context.Background(), "", userId, domain, false)
manager, err := createManager(t) manager, err := createManager(t)
require.NoError(t, err, "unable to create account manager") require.NoError(t, err, "unable to create account manager")
accountID, err := manager.GetAccountIDByUserID(context.Background(), userId, domain) accountID, err := manager.GetAccountIDByUserID(context.Background(), userId, domain)
@@ -793,7 +793,7 @@ func TestAccountManager_GetAccountByUserID(t *testing.T) {
} }
func createAccount(am *DefaultAccountManager, accountID, userID, domain string) (*types.Account, error) { func createAccount(am *DefaultAccountManager, accountID, userID, domain string) (*types.Account, error) {
account := newAccountWithId(context.Background(), accountID, userID, domain) account := newAccountWithId(context.Background(), accountID, userID, domain, false)
err := am.Store.SaveAccount(context.Background(), account) err := am.Store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1208,6 +1208,14 @@ func TestAccountManager_NetworkUpdates_DeletePolicy(t *testing.T) {
updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID) updMsg := manager.peersUpdateManager.CreateChannel(context.Background(), peer1.ID)
defer manager.peersUpdateManager.CloseChannel(context.Background(), peer1.ID) defer manager.peersUpdateManager.CloseChannel(context.Background(), peer1.ID)
// Ensure that we do not receive an update message before the policy is deleted
time.Sleep(time.Second)
select {
case <-updMsg:
t.Logf("received addPeer update message before policy deletion")
default:
}
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(1) wg.Add(1)
go func() { go func() {
@@ -2879,7 +2887,7 @@ func createManager(t testing.TB) (*DefaultAccountManager, error) {
permissionsManager := permissions.NewManager(store) permissionsManager := permissions.NewManager(store)
manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+2 -2
View File
@@ -217,7 +217,7 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
settingsMockManager := settings.NewMockManager(ctrl) settingsMockManager := settings.NewMockManager(ctrl)
permissionsManager := permissions.NewManager(store) permissionsManager := permissions.NewManager(store)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
} }
func createDNSStore(t *testing.T) (store.Store, error) { func createDNSStore(t *testing.T) (store.Store, error) {
@@ -267,7 +267,7 @@ func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*types.Account
domain := "example.com" domain := "example.com"
account := newAccountWithId(context.Background(), dnsAccountID, dnsAdminUserID, domain) account := newAccountWithId(context.Background(), dnsAccountID, dnsAdminUserID, domain, false)
account.Users[dnsRegularUserID] = &types.User{ account.Users[dnsRegularUserID] = &types.User{
Id: dnsRegularUserID, Id: dnsRegularUserID,
+1 -1
View File
@@ -127,7 +127,7 @@ func TestNewManagerPeerDisconnected(t *testing.T) {
} }
func seedPeers(store *MockStore, numberOfPeers int, numberOfEphemeralPeers int) { func seedPeers(store *MockStore, numberOfPeers int, numberOfEphemeralPeers int) {
store.account = newAccountWithId(context.Background(), "my account", "", "") store.account = newAccountWithId(context.Background(), "my account", "", "", false)
for i := 0; i < numberOfPeers; i++ { for i := 0; i < numberOfPeers; i++ {
peerId := fmt.Sprintf("peer_%d", i) peerId := fmt.Sprintf("peer_%d", i)
+1 -1
View File
@@ -369,7 +369,7 @@ func initTestGroupAccount(am *DefaultAccountManager) (*DefaultAccountManager, *t
Id: "example user", Id: "example user",
AutoGroups: []string{groupForUsers.ID}, AutoGroups: []string{groupForUsers.ID},
} }
account := newAccountWithId(context.Background(), accountID, groupAdminUserID, domain) account := newAccountWithId(context.Background(), accountID, groupAdminUserID, domain, false)
account.Routes[routeResource.ID] = routeResource account.Routes[routeResource.ID] = routeResource
account.Routes[routePeerGroupResource.ID] = routePeerGroupResource account.Routes[routePeerGroupResource.ID] = routePeerGroupResource
account.NameServerGroups[nameServerGroup.ID] = nameServerGroup account.NameServerGroups[nameServerGroup.ID] = nameServerGroup
+5
View File
@@ -426,6 +426,10 @@ components:
items: items:
type: string type: string
example: "stage-host-1" example: "stage-host-1"
ephemeral:
description: Indicates whether the peer is ephemeral or not
type: boolean
example: false
required: required:
- city_name - city_name
- connected - connected
@@ -450,6 +454,7 @@ components:
- approval_required - approval_required
- serial_number - serial_number
- extra_dns_labels - extra_dns_labels
- ephemeral
AccessiblePeer: AccessiblePeer:
allOf: allOf:
- $ref: '#/components/schemas/PeerMinimum' - $ref: '#/components/schemas/PeerMinimum'
+6
View File
@@ -1016,6 +1016,9 @@ type Peer struct {
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud // DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
DnsLabel string `json:"dns_label"` DnsLabel string `json:"dns_label"`
// Ephemeral Indicates whether the peer is ephemeral or not
Ephemeral bool `json:"ephemeral"`
// ExtraDnsLabels Extra DNS labels added to the peer // ExtraDnsLabels Extra DNS labels added to the peer
ExtraDnsLabels []string `json:"extra_dns_labels"` ExtraDnsLabels []string `json:"extra_dns_labels"`
@@ -1097,6 +1100,9 @@ type PeerBatch struct {
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud // DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
DnsLabel string `json:"dns_label"` DnsLabel string `json:"dns_label"`
// Ephemeral Indicates whether the peer is ephemeral or not
Ephemeral bool `json:"ephemeral"`
// ExtraDnsLabels Extra DNS labels added to the peer // ExtraDnsLabels Extra DNS labels added to the peer
ExtraDnsLabels []string `json:"extra_dns_labels"` ExtraDnsLabels []string `json:"extra_dns_labels"`
@@ -365,6 +365,7 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD
CityName: peer.Location.CityName, CityName: peer.Location.CityName,
SerialNumber: peer.Meta.SystemSerialNumber, SerialNumber: peer.Meta.SystemSerialNumber,
InactivityExpirationEnabled: peer.InactivityExpirationEnabled, InactivityExpirationEnabled: peer.InactivityExpirationEnabled,
Ephemeral: peer.Ephemeral,
} }
} }
@@ -1,5 +1,4 @@
package testing_tools package testing_tools
import ( import (
"bytes" "bytes"
"context" "context"
@@ -138,7 +137,7 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
userManager := users.NewManager(store) userManager := users.NewManager(store)
permissionsManager := permissions.NewManager(store) permissionsManager := permissions.NewManager(store)
settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}), permissionsManager) settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}), permissionsManager)
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManager) am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManager, false)
if err != nil { if err != nil {
t.Fatalf("Failed to create manager: %v", err) t.Fatalf("Failed to create manager: %v", err)
} }
+20 -21
View File
@@ -37,21 +37,23 @@ func (am *DefaultAccountManager) UpdateIntegratedValidatorGroups(ctx context.Con
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID) unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
defer unlock() defer unlock()
a, err := am.Store.GetAccountByUser(ctx, userID) return am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
if err != nil { a, err := transaction.GetAccountByUser(ctx, userID)
return err if err != nil {
} return err
}
var extra *types.ExtraSettings var extra *types.ExtraSettings
if a.Settings.Extra != nil { if a.Settings.Extra != nil {
extra = a.Settings.Extra extra = a.Settings.Extra
} else { } else {
extra = &types.ExtraSettings{} extra = &types.ExtraSettings{}
a.Settings.Extra = extra a.Settings.Extra = extra
} }
extra.IntegratedValidatorGroups = groups extra.IntegratedValidatorGroups = groups
return am.Store.SaveAccount(ctx, a) return transaction.SaveAccount(ctx, a)
})
} }
func (am *DefaultAccountManager) GroupValidation(ctx context.Context, accountID string, groupIDs []string) (bool, error) { func (am *DefaultAccountManager) GroupValidation(ctx context.Context, accountID string, groupIDs []string) (bool, error) {
@@ -81,15 +83,12 @@ func (am *DefaultAccountManager) GetValidatedPeers(ctx context.Context, accountI
var peers []*nbpeer.Peer var peers []*nbpeer.Peer
var settings *types.Settings var settings *types.Settings
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error { groups, err = am.Store.GetAccountGroups(ctx, store.LockingStrengthShare, accountID)
groups, err = transaction.GetAccountGroups(ctx, store.LockingStrengthShare, accountID) if err != nil {
if err != nil { return nil, err
return err }
}
peers, err = transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "") peers, err = am.Store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "")
return err
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
+1 -1
View File
@@ -444,7 +444,7 @@ func startManagementForTest(t *testing.T, testFile string, config *types.Config)
permissionsManager := permissions.NewManager(store) permissionsManager := permissions.NewManager(store)
accountManager, err := BuildManager(ctx, store, peersUpdateManager, nil, "", "netbird.selfhosted", accountManager, err := BuildManager(ctx, store, peersUpdateManager, nil, "", "netbird.selfhosted",
eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
if err != nil { if err != nil {
cleanup() cleanup()
+1 -1
View File
@@ -211,7 +211,7 @@ func startServer(
port_forwarding.NewControllerMock(), port_forwarding.NewControllerMock(),
settingsMockManager, settingsMockManager,
permissionsManager, permissionsManager,
) false)
if err != nil { if err != nil {
t.Fatalf("failed creating an account manager: %v", err) t.Fatalf("failed creating an account manager: %v", err)
} }
+16 -1
View File
@@ -184,7 +184,9 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
ephemeralPeersSKs int ephemeralPeersSKs int
ephemeralPeersSKUsage int ephemeralPeersSKUsage int
activePeersLastDay int activePeersLastDay int
activeUserPeersLastDay int
osPeers map[string]int osPeers map[string]int
activeUsersLastDay map[string]struct{}
userPeers int userPeers int
rules int rules int
rulesProtocol map[string]int rulesProtocol map[string]int
@@ -203,6 +205,7 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
version string version string
peerActiveVersions []string peerActiveVersions []string
osUIClients map[string]int osUIClients map[string]int
rosenpassEnabled int
) )
start := time.Now() start := time.Now()
metricsProperties := make(properties) metricsProperties := make(properties)
@@ -210,6 +213,7 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
osUIClients = make(map[string]int) osUIClients = make(map[string]int)
rulesProtocol = make(map[string]int) rulesProtocol = make(map[string]int)
rulesDirection = make(map[string]int) rulesDirection = make(map[string]int)
activeUsersLastDay = make(map[string]struct{})
uptime = time.Since(w.startupTime).Seconds() uptime = time.Since(w.startupTime).Seconds()
connections := w.connManager.GetAllConnectedPeers() connections := w.connManager.GetAllConnectedPeers()
version = nbversion.NetbirdVersion() version = nbversion.NetbirdVersion()
@@ -277,10 +281,14 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
for _, peer := range account.Peers { for _, peer := range account.Peers {
peers++ peers++
if peer.SSHEnabled { if peer.SSHEnabled || peer.Meta.Flags.ServerSSHAllowed {
peersSSHEnabled++ peersSSHEnabled++
} }
if peer.Meta.Flags.RosenpassEnabled {
rosenpassEnabled++
}
if peer.UserID != "" { if peer.UserID != "" {
userPeers++ userPeers++
} }
@@ -299,6 +307,10 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
_, connected := connections[peer.ID] _, connected := connections[peer.ID]
if connected || peer.Status.LastSeen.After(w.lastRun) { if connected || peer.Status.LastSeen.After(w.lastRun) {
activePeersLastDay++ activePeersLastDay++
if peer.UserID != "" {
activeUserPeersLastDay++
activeUsersLastDay[peer.UserID] = struct{}{}
}
osActiveKey := osKey + "_active" osActiveKey := osKey + "_active"
osActiveCount := osPeers[osActiveKey] osActiveCount := osPeers[osActiveKey]
osPeers[osActiveKey] = osActiveCount + 1 osPeers[osActiveKey] = osActiveCount + 1
@@ -320,6 +332,8 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
metricsProperties["ephemeral_peers_setup_keys"] = ephemeralPeersSKs metricsProperties["ephemeral_peers_setup_keys"] = ephemeralPeersSKs
metricsProperties["ephemeral_peers_setup_keys_usage"] = ephemeralPeersSKUsage metricsProperties["ephemeral_peers_setup_keys_usage"] = ephemeralPeersSKUsage
metricsProperties["active_peers_last_day"] = activePeersLastDay metricsProperties["active_peers_last_day"] = activePeersLastDay
metricsProperties["active_user_peers_last_day"] = activeUserPeersLastDay
metricsProperties["active_users_last_day"] = len(activeUsersLastDay)
metricsProperties["user_peers"] = userPeers metricsProperties["user_peers"] = userPeers
metricsProperties["rules"] = rules metricsProperties["rules"] = rules
metricsProperties["rules_with_src_posture_checks"] = rulesWithSrcPostureChecks metricsProperties["rules_with_src_posture_checks"] = rulesWithSrcPostureChecks
@@ -338,6 +352,7 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
metricsProperties["ui_clients"] = uiClient metricsProperties["ui_clients"] = uiClient
metricsProperties["idp_manager"] = w.idpManager metricsProperties["idp_manager"] = w.idpManager
metricsProperties["store_engine"] = w.dataSource.GetStoreEngine() metricsProperties["store_engine"] = w.dataSource.GetStoreEngine()
metricsProperties["rosenpass_enabled"] = rosenpassEnabled
for protocol, count := range rulesProtocol { for protocol, count := range rulesProtocol {
metricsProperties["rules_protocol_"+protocol] = count metricsProperties["rules_protocol_"+protocol] = count
+15 -3
View File
@@ -47,8 +47,8 @@ func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
"1": { "1": {
ID: "1", ID: "1",
UserID: "test", UserID: "test",
SSHEnabled: true, SSHEnabled: false,
Meta: nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1"}, Meta: nbpeer.PeerSystemMeta{GoOS: "linux", WtVersion: "0.0.1", Flags: nbpeer.Flags{ServerSSHAllowed: true, RosenpassEnabled: true}},
}, },
}, },
Policies: []*types.Policy{ Policies: []*types.Policy{
@@ -312,7 +312,19 @@ func TestGenerateProperties(t *testing.T) {
} }
if properties["posture_checks"] != 2 { if properties["posture_checks"] != 2 {
t.Errorf("expected 1 posture_checks, got %d", properties["posture_checks"]) t.Errorf("expected 2 posture_checks, got %d", properties["posture_checks"])
}
if properties["rosenpass_enabled"] != 1 {
t.Errorf("expected 1 rosenpass_enabled, got %d", properties["rosenpass_enabled"])
}
if properties["active_user_peers_last_day"] != 2 {
t.Errorf("expected 2 active_user_peers_last_day, got %d", properties["active_user_peers_last_day"])
}
if properties["active_users_last_day"] != 1 {
t.Errorf("expected 1 active_users_last_day, got %d", properties["active_users_last_day"])
} }
} }
+2 -2
View File
@@ -779,7 +779,7 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
t.Cleanup(ctrl.Finish) t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl) settingsMockManager := settings.NewMockManager(ctrl)
permissionsManager := permissions.NewManager(store) permissionsManager := permissions.NewManager(store)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
} }
func createNSStore(t *testing.T) (store.Store, error) { func createNSStore(t *testing.T) (store.Store, error) {
@@ -848,7 +848,7 @@ func initTestNSAccount(t *testing.T, am *DefaultAccountManager) (*types.Account,
userID := testUserID userID := testUserID
domain := "example.com" domain := "example.com"
account := newAccountWithId(context.Background(), accountID, userID, domain) account := newAccountWithId(context.Background(), accountID, userID, domain, false)
account.NameServerGroups[existingNSGroup.ID] = &existingNSGroup account.NameServerGroups[existingNSGroup.ID] = &existingNSGroup
+16 -4
View File
@@ -92,7 +92,7 @@ func (am *DefaultAccountManager) getUserAccessiblePeers(ctx context.Context, acc
// fetch all the peers that have access to the user's peers // fetch all the peers that have access to the user's peers
for _, peer := range peers { for _, peer := range peers {
aclPeers, _ := account.GetPeerConnectionResources(ctx, peer.ID, approvedPeersMap) aclPeers, _ := account.GetPeerConnectionResources(ctx, peer, approvedPeersMap)
for _, p := range aclPeers { for _, p := range aclPeers {
peersMap[p.ID] = p peersMap[p.ID] = p
} }
@@ -1149,7 +1149,7 @@ func (am *DefaultAccountManager) checkIfUserOwnsPeer(ctx context.Context, accoun
} }
for _, p := range userPeers { for _, p := range userPeers {
aclPeers, _ := account.GetPeerConnectionResources(ctx, p.ID, approvedPeersMap) aclPeers, _ := account.GetPeerConnectionResources(ctx, p, approvedPeersMap)
for _, aclPeer := range aclPeers { for _, aclPeer := range aclPeers {
if aclPeer.ID == peer.ID { if aclPeer.ID == peer.ID {
return peer, nil return peer, nil
@@ -1169,7 +1169,7 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
return return
} }
start := time.Now() globalStart := time.Now()
approvedPeersMap, err := am.integratedPeerValidator.GetValidatedPeers(account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra) approvedPeersMap, err := am.integratedPeerValidator.GetValidatedPeers(account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
if err != nil { if err != nil {
@@ -1204,18 +1204,27 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
defer wg.Done() defer wg.Done()
defer func() { <-semaphore }() defer func() { <-semaphore }()
start := time.Now()
postureChecks, err := am.getPeerPostureChecks(account, p.ID) postureChecks, err := am.getPeerPostureChecks(account, p.ID)
if err != nil { if err != nil {
log.WithContext(ctx).Debugf("failed to get posture checks for peer %s: %v", peer.ID, err) log.WithContext(ctx).Debugf("failed to get posture checks for peer %s: %v", peer.ID, err)
return return
} }
am.metrics.UpdateChannelMetrics().CountCalcPostureChecksDuration(time.Since(start))
start = time.Now()
remotePeerNetworkMap := account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics()) remotePeerNetworkMap := account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics())
am.metrics.UpdateChannelMetrics().CountCalcPeerNetworkMapDuration(time.Since(start))
start = time.Now()
proxyNetworkMap, ok := proxyNetworkMaps[p.ID] proxyNetworkMap, ok := proxyNetworkMaps[p.ID]
if ok { if ok {
remotePeerNetworkMap.Merge(proxyNetworkMap) remotePeerNetworkMap.Merge(proxyNetworkMap)
} }
am.metrics.UpdateChannelMetrics().CountMergeNetworkMapDuration(time.Since(start))
extraSetting, err := am.settingsManager.GetExtraSettings(ctx, accountID) extraSetting, err := am.settingsManager.GetExtraSettings(ctx, accountID)
if err != nil { if err != nil {
@@ -1223,7 +1232,10 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
return return
} }
start = time.Now()
update := toSyncResponse(ctx, nil, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting) update := toSyncResponse(ctx, nil, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting)
am.metrics.UpdateChannelMetrics().CountToSyncResponseDuration(time.Since(start))
am.peersUpdateManager.SendUpdate(ctx, p.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap}) am.peersUpdateManager.SendUpdate(ctx, p.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap})
}(peer) }(peer)
} }
@@ -1232,7 +1244,7 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
wg.Wait() wg.Wait()
if am.metrics != nil { if am.metrics != nil {
am.metrics.AccountManagerMetrics().CountUpdateAccountPeersDuration(time.Since(start)) am.metrics.AccountManagerMetrics().CountUpdateAccountPeersDuration(time.Since(globalStart))
} }
} }
+8 -8
View File
@@ -480,7 +480,7 @@ func TestDefaultAccountManager_GetPeer(t *testing.T) {
accountID := "test_account" accountID := "test_account"
adminUser := "account_creator" adminUser := "account_creator"
someUser := "some_user" someUser := "some_user"
account := newAccountWithId(context.Background(), accountID, adminUser, "") account := newAccountWithId(context.Background(), accountID, adminUser, "", false)
account.Users[someUser] = &types.User{ account.Users[someUser] = &types.User{
Id: someUser, Id: someUser,
Role: types.UserRoleUser, Role: types.UserRoleUser,
@@ -667,7 +667,7 @@ func TestDefaultAccountManager_GetPeers(t *testing.T) {
accountID := "test_account" accountID := "test_account"
adminUser := "account_creator" adminUser := "account_creator"
someUser := "some_user" someUser := "some_user"
account := newAccountWithId(context.Background(), accountID, adminUser, "") account := newAccountWithId(context.Background(), accountID, adminUser, "", false)
account.Users[someUser] = &types.User{ account.Users[someUser] = &types.User{
Id: someUser, Id: someUser,
Role: testCase.role, Role: testCase.role,
@@ -737,7 +737,7 @@ func setupTestAccountManager(b testing.TB, peers int, groups int) (*DefaultAccou
adminUser := "account_creator" adminUser := "account_creator"
regularUser := "regular_user" regularUser := "regular_user"
account := newAccountWithId(context.Background(), accountID, adminUser, "") account := newAccountWithId(context.Background(), accountID, adminUser, "", false)
account.Users[regularUser] = &types.User{ account.Users[regularUser] = &types.User{
Id: regularUser, Id: regularUser,
Role: types.UserRoleUser, Role: types.UserRoleUser,
@@ -1267,7 +1267,7 @@ func Test_RegisterPeerByUser(t *testing.T) {
settingsMockManager := settings.NewMockManager(ctrl) settingsMockManager := settings.NewMockManager(ctrl)
permissionsManager := permissions.NewManager(s) permissionsManager := permissions.NewManager(s)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
assert.NoError(t, err) assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1342,7 +1342,7 @@ func Test_RegisterPeerBySetupKey(t *testing.T) {
settingsMockManager := settings.NewMockManager(ctrl) settingsMockManager := settings.NewMockManager(ctrl)
permissionsManager := permissions.NewManager(s) permissionsManager := permissions.NewManager(s)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
assert.NoError(t, err) assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1477,7 +1477,7 @@ func Test_RegisterPeerRollbackOnFailure(t *testing.T) {
permissionsManager := permissions.NewManager(s) permissionsManager := permissions.NewManager(s)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
assert.NoError(t, err) assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1546,7 +1546,7 @@ func Test_LoginPeer(t *testing.T) {
settingsMockManager := settings.NewMockManager(ctrl) settingsMockManager := settings.NewMockManager(ctrl)
permissionsManager := permissions.NewManager(s) permissionsManager := permissions.NewManager(s)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
assert.NoError(t, err) assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -2052,7 +2052,7 @@ func Test_DeletePeer(t *testing.T) {
// account with an admin and a regular user // account with an admin and a regular user
accountID := "test_account" accountID := "test_account"
adminUser := "account_creator" adminUser := "account_creator"
account := newAccountWithId(context.Background(), accountID, adminUser, "") account := newAccountWithId(context.Background(), accountID, adminUser, "", false)
account.Peers = map[string]*nbpeer.Peer{ account.Peers = map[string]*nbpeer.Peer{
"peer1": { "peer1": {
ID: "peer1", ID: "peer1",
+106 -33
View File
@@ -27,6 +27,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
ID: "peerB", ID: "peerB",
IP: net.ParseIP("100.65.80.39"), IP: net.ParseIP("100.65.80.39"),
Status: &nbpeer.PeerStatus{}, Status: &nbpeer.PeerStatus{},
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.48.0"},
}, },
"peerC": { "peerC": {
ID: "peerC", ID: "peerC",
@@ -63,6 +64,12 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
IP: net.ParseIP("100.65.31.2"), IP: net.ParseIP("100.65.31.2"),
Status: &nbpeer.PeerStatus{}, Status: &nbpeer.PeerStatus{},
}, },
"peerK": {
ID: "peerK",
IP: net.ParseIP("100.32.80.1"),
Status: &nbpeer.PeerStatus{},
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.30.0"},
},
}, },
Groups: map[string]*types.Group{ Groups: map[string]*types.Group{
"GroupAll": { "GroupAll": {
@@ -111,6 +118,13 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
"peerI", "peerI",
}, },
}, },
"GroupWorkflow": {
ID: "GroupWorkflow",
Name: "workflow",
Peers: []string{
"peerK",
},
},
}, },
Policies: []*types.Policy{ Policies: []*types.Policy{
{ {
@@ -189,6 +203,39 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
}, },
}, },
}, },
{
ID: "RuleWorkflow",
Name: "Workflow",
Description: "No description",
Enabled: true,
Rules: []*types.PolicyRule{
{
ID: "RuleWorkflow",
Name: "Workflow",
Description: "No description",
Bidirectional: true,
Enabled: true,
Protocol: types.PolicyRuleProtocolTCP,
Action: types.PolicyTrafficActionAccept,
PortRanges: []types.RulePortRange{
{
Start: 8088,
End: 8088,
},
{
Start: 9090,
End: 9095,
},
},
Sources: []string{
"GroupWorkflow",
},
Destinations: []string{
"GroupDMZ",
},
},
},
},
}, },
} }
@@ -199,14 +246,14 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
t.Run("check that all peers get map", func(t *testing.T) { t.Run("check that all peers get map", func(t *testing.T) {
for _, p := range account.Peers { for _, p := range account.Peers {
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), p.ID, validatedPeers) peers, firewallRules := account.GetPeerConnectionResources(context.Background(), p, validatedPeers)
assert.GreaterOrEqual(t, len(peers), 2, "minimum number peers should present") assert.GreaterOrEqual(t, len(peers), 1, "minimum number peers should present")
assert.GreaterOrEqual(t, len(firewallRules), 2, "minimum number of firewall rules should present") assert.GreaterOrEqual(t, len(firewallRules), 1, "minimum number of firewall rules should present")
} }
}) })
t.Run("check first peer map details", func(t *testing.T) { t.Run("check first peer map details", func(t *testing.T) {
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", validatedPeers) peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], validatedPeers)
assert.Len(t, peers, 8) assert.Len(t, peers, 8)
assert.Contains(t, peers, account.Peers["peerA"]) assert.Contains(t, peers, account.Peers["peerA"])
assert.Contains(t, peers, account.Peers["peerC"]) assert.Contains(t, peers, account.Peers["peerC"])
@@ -364,6 +411,32 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
assert.True(t, contains, "rule not found in expected rules %#v", rule) assert.True(t, contains, "rule not found in expected rules %#v", rule)
} }
}) })
t.Run("check port ranges support for older peers", func(t *testing.T) {
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerK"], validatedPeers)
assert.Len(t, peers, 1)
assert.Contains(t, peers, account.Peers["peerI"])
expectedFirewallRules := []*types.FirewallRule{
{
PeerIP: "100.65.31.2",
Direction: types.FirewallRuleDirectionIN,
Action: "accept",
Protocol: "tcp",
Port: "8088",
PolicyID: "RuleWorkflow",
},
{
PeerIP: "100.65.31.2",
Direction: types.FirewallRuleDirectionOUT,
Action: "accept",
Protocol: "tcp",
Port: "8088",
PolicyID: "RuleWorkflow",
},
}
assert.ElementsMatch(t, firewallRules, expectedFirewallRules)
})
} }
func TestAccount_getPeersByPolicyDirect(t *testing.T) { func TestAccount_getPeersByPolicyDirect(t *testing.T) {
@@ -466,10 +539,10 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
} }
t.Run("check first peer map", func(t *testing.T) { t.Run("check first peer map", func(t *testing.T) {
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", approvedPeers) peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], approvedPeers)
assert.Contains(t, peers, account.Peers["peerC"]) assert.Contains(t, peers, account.Peers["peerC"])
epectedFirewallRules := []*types.FirewallRule{ expectedFirewallRules := []*types.FirewallRule{
{ {
PeerIP: "100.65.254.139", PeerIP: "100.65.254.139",
Direction: types.FirewallRuleDirectionIN, Direction: types.FirewallRuleDirectionIN,
@@ -487,19 +560,19 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
PolicyID: "RuleSwarm", PolicyID: "RuleSwarm",
}, },
} }
assert.Len(t, firewallRules, len(epectedFirewallRules)) assert.Len(t, firewallRules, len(expectedFirewallRules))
slices.SortFunc(epectedFirewallRules, sortFunc()) slices.SortFunc(expectedFirewallRules, sortFunc())
slices.SortFunc(firewallRules, sortFunc()) slices.SortFunc(firewallRules, sortFunc())
for i := range firewallRules { for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i]) assert.Equal(t, expectedFirewallRules[i], firewallRules[i])
} }
}) })
t.Run("check second peer map", func(t *testing.T) { t.Run("check second peer map", func(t *testing.T) {
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerC", approvedPeers) peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerC"], approvedPeers)
assert.Contains(t, peers, account.Peers["peerB"]) assert.Contains(t, peers, account.Peers["peerB"])
epectedFirewallRules := []*types.FirewallRule{ expectedFirewallRules := []*types.FirewallRule{
{ {
PeerIP: "100.65.80.39", PeerIP: "100.65.80.39",
Direction: types.FirewallRuleDirectionIN, Direction: types.FirewallRuleDirectionIN,
@@ -517,21 +590,21 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
PolicyID: "RuleSwarm", PolicyID: "RuleSwarm",
}, },
} }
assert.Len(t, firewallRules, len(epectedFirewallRules)) assert.Len(t, firewallRules, len(expectedFirewallRules))
slices.SortFunc(epectedFirewallRules, sortFunc()) slices.SortFunc(expectedFirewallRules, sortFunc())
slices.SortFunc(firewallRules, sortFunc()) slices.SortFunc(firewallRules, sortFunc())
for i := range firewallRules { for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i]) assert.Equal(t, expectedFirewallRules[i], firewallRules[i])
} }
}) })
account.Policies[1].Rules[0].Bidirectional = false account.Policies[1].Rules[0].Bidirectional = false
t.Run("check first peer map directional only", func(t *testing.T) { t.Run("check first peer map directional only", func(t *testing.T) {
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", approvedPeers) peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], approvedPeers)
assert.Contains(t, peers, account.Peers["peerC"]) assert.Contains(t, peers, account.Peers["peerC"])
epectedFirewallRules := []*types.FirewallRule{ expectedFirewallRules := []*types.FirewallRule{
{ {
PeerIP: "100.65.254.139", PeerIP: "100.65.254.139",
Direction: types.FirewallRuleDirectionOUT, Direction: types.FirewallRuleDirectionOUT,
@@ -541,19 +614,19 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
PolicyID: "RuleSwarm", PolicyID: "RuleSwarm",
}, },
} }
assert.Len(t, firewallRules, len(epectedFirewallRules)) assert.Len(t, firewallRules, len(expectedFirewallRules))
slices.SortFunc(epectedFirewallRules, sortFunc()) slices.SortFunc(expectedFirewallRules, sortFunc())
slices.SortFunc(firewallRules, sortFunc()) slices.SortFunc(firewallRules, sortFunc())
for i := range firewallRules { for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i]) assert.Equal(t, expectedFirewallRules[i], firewallRules[i])
} }
}) })
t.Run("check second peer map directional only", func(t *testing.T) { t.Run("check second peer map directional only", func(t *testing.T) {
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerC", approvedPeers) peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerC"], approvedPeers)
assert.Contains(t, peers, account.Peers["peerB"]) assert.Contains(t, peers, account.Peers["peerB"])
epectedFirewallRules := []*types.FirewallRule{ expectedFirewallRules := []*types.FirewallRule{
{ {
PeerIP: "100.65.80.39", PeerIP: "100.65.80.39",
Direction: types.FirewallRuleDirectionIN, Direction: types.FirewallRuleDirectionIN,
@@ -563,11 +636,11 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
PolicyID: "RuleSwarm", PolicyID: "RuleSwarm",
}, },
} }
assert.Len(t, firewallRules, len(epectedFirewallRules)) assert.Len(t, firewallRules, len(expectedFirewallRules))
slices.SortFunc(epectedFirewallRules, sortFunc()) slices.SortFunc(expectedFirewallRules, sortFunc())
slices.SortFunc(firewallRules, sortFunc()) slices.SortFunc(firewallRules, sortFunc())
for i := range firewallRules { for i := range firewallRules {
assert.Equal(t, epectedFirewallRules[i], firewallRules[i]) assert.Equal(t, expectedFirewallRules[i], firewallRules[i])
} }
}) })
} }
@@ -748,7 +821,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
t.Run("verify peer's network map with default group peer list", func(t *testing.T) { t.Run("verify peer's network map with default group peer list", func(t *testing.T) {
// peerB doesn't fulfill the NB posture check but is included in the destination group Swarm, // peerB doesn't fulfill the NB posture check but is included in the destination group Swarm,
// will establish a connection with all source peers satisfying the NB posture check. // will establish a connection with all source peers satisfying the NB posture check.
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", approvedPeers) peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], approvedPeers)
assert.Len(t, peers, 4) assert.Len(t, peers, 4)
assert.Len(t, firewallRules, 4) assert.Len(t, firewallRules, 4)
assert.Contains(t, peers, account.Peers["peerA"]) assert.Contains(t, peers, account.Peers["peerA"])
@@ -758,7 +831,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
// peerC satisfy the NB posture check, should establish connection to all destination group peer's // peerC satisfy the NB posture check, should establish connection to all destination group peer's
// We expect a single permissive firewall rule which all outgoing connections // We expect a single permissive firewall rule which all outgoing connections
peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerC", approvedPeers) peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerC"], approvedPeers)
assert.Len(t, peers, len(account.Groups["GroupSwarm"].Peers)) assert.Len(t, peers, len(account.Groups["GroupSwarm"].Peers))
assert.Len(t, firewallRules, 1) assert.Len(t, firewallRules, 1)
expectedFirewallRules := []*types.FirewallRule{ expectedFirewallRules := []*types.FirewallRule{
@@ -775,7 +848,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
// peerE doesn't fulfill the NB posture check and exists in only destination group Swarm, // peerE doesn't fulfill the NB posture check and exists in only destination group Swarm,
// all source group peers satisfying the NB posture check should establish connection // all source group peers satisfying the NB posture check should establish connection
peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerE", approvedPeers) peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerE"], approvedPeers)
assert.Len(t, peers, 4) assert.Len(t, peers, 4)
assert.Len(t, firewallRules, 4) assert.Len(t, firewallRules, 4)
assert.Contains(t, peers, account.Peers["peerA"]) assert.Contains(t, peers, account.Peers["peerA"])
@@ -785,7 +858,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
// peerI doesn't fulfill the OS version posture check and exists in only destination group Swarm, // peerI doesn't fulfill the OS version posture check and exists in only destination group Swarm,
// all source group peers satisfying the NB posture check should establish connection // all source group peers satisfying the NB posture check should establish connection
peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerI", approvedPeers) peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerI"], approvedPeers)
assert.Len(t, peers, 4) assert.Len(t, peers, 4)
assert.Len(t, firewallRules, 4) assert.Len(t, firewallRules, 4)
assert.Contains(t, peers, account.Peers["peerA"]) assert.Contains(t, peers, account.Peers["peerA"])
@@ -800,19 +873,19 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
// peerB doesn't satisfy the NB posture check, and doesn't exist in destination group peer's // peerB doesn't satisfy the NB posture check, and doesn't exist in destination group peer's
// no connection should be established to any peer of destination group // no connection should be established to any peer of destination group
peers, firewallRules := account.GetPeerConnectionResources(context.Background(), "peerB", approvedPeers) peers, firewallRules := account.GetPeerConnectionResources(context.Background(), account.Peers["peerB"], approvedPeers)
assert.Len(t, peers, 0) assert.Len(t, peers, 0)
assert.Len(t, firewallRules, 0) assert.Len(t, firewallRules, 0)
// peerI doesn't satisfy the OS version posture check, and doesn't exist in destination group peer's // peerI doesn't satisfy the OS version posture check, and doesn't exist in destination group peer's
// no connection should be established to any peer of destination group // no connection should be established to any peer of destination group
peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerI", approvedPeers) peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerI"], approvedPeers)
assert.Len(t, peers, 0) assert.Len(t, peers, 0)
assert.Len(t, firewallRules, 0) assert.Len(t, firewallRules, 0)
// peerC satisfy the NB posture check, should establish connection to all destination group peer's // peerC satisfy the NB posture check, should establish connection to all destination group peer's
// We expect a single permissive firewall rule which all outgoing connections // We expect a single permissive firewall rule which all outgoing connections
peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerC", approvedPeers) peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerC"], approvedPeers)
assert.Len(t, peers, len(account.Groups["GroupSwarm"].Peers)) assert.Len(t, peers, len(account.Groups["GroupSwarm"].Peers))
assert.Len(t, firewallRules, len(account.Groups["GroupSwarm"].Peers)) assert.Len(t, firewallRules, len(account.Groups["GroupSwarm"].Peers))
@@ -827,14 +900,14 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
// peerE doesn't fulfill the NB posture check and exists in only destination group Swarm, // peerE doesn't fulfill the NB posture check and exists in only destination group Swarm,
// all source group peers satisfying the NB posture check should establish connection // all source group peers satisfying the NB posture check should establish connection
peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerE", approvedPeers) peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerE"], approvedPeers)
assert.Len(t, peers, 3) assert.Len(t, peers, 3)
assert.Len(t, firewallRules, 3) assert.Len(t, firewallRules, 3)
assert.Contains(t, peers, account.Peers["peerA"]) assert.Contains(t, peers, account.Peers["peerA"])
assert.Contains(t, peers, account.Peers["peerC"]) assert.Contains(t, peers, account.Peers["peerC"])
assert.Contains(t, peers, account.Peers["peerD"]) assert.Contains(t, peers, account.Peers["peerD"])
peers, firewallRules = account.GetPeerConnectionResources(context.Background(), "peerA", approvedPeers) peers, firewallRules = account.GetPeerConnectionResources(context.Background(), account.Peers["peerA"], approvedPeers)
assert.Len(t, peers, 5) assert.Len(t, peers, 5)
// assert peers from Group Swarm // assert peers from Group Swarm
assert.Contains(t, peers, account.Peers["peerD"]) assert.Contains(t, peers, account.Peers["peerD"])
+20 -10
View File
@@ -24,20 +24,12 @@ func sanitizeVersion(version string) string {
} }
func (n *NBVersionCheck) Check(ctx context.Context, peer nbpeer.Peer) (bool, error) { func (n *NBVersionCheck) Check(ctx context.Context, peer nbpeer.Peer) (bool, error) {
peerVersion := sanitizeVersion(peer.Meta.WtVersion) meetsMin, err := MeetsMinVersion(n.MinVersion, peer.Meta.WtVersion)
minVersion := sanitizeVersion(n.MinVersion)
peerNBVersion, err := version.NewVersion(peerVersion)
if err != nil { if err != nil {
return false, err return false, err
} }
constraints, err := version.NewConstraint(">= " + minVersion) if meetsMin {
if err != nil {
return false, err
}
if constraints.Check(peerNBVersion) {
return true, nil return true, nil
} }
@@ -60,3 +52,21 @@ func (n *NBVersionCheck) Validate() error {
} }
return nil return nil
} }
// MeetsMinVersion checks if the peer's version meets or exceeds the minimum required version
func MeetsMinVersion(minVer, peerVer string) (bool, error) {
peerVer = sanitizeVersion(peerVer)
minVer = sanitizeVersion(minVer)
peerNBVer, err := version.NewVersion(peerVer)
if err != nil {
return false, err
}
constraints, err := version.NewConstraint(">= " + minVer)
if err != nil {
return false, err
}
return constraints.Check(peerNBVer), nil
}
@@ -139,3 +139,68 @@ func TestNBVersionCheck_Validate(t *testing.T) {
}) })
} }
} }
func TestMeetsMinVersion(t *testing.T) {
tests := []struct {
name string
minVer string
peerVer string
want bool
wantErr bool
}{
{
name: "Peer version greater than min version",
minVer: "0.26.0",
peerVer: "0.60.1",
want: true,
wantErr: false,
},
{
name: "Peer version equals min version",
minVer: "1.0.0",
peerVer: "1.0.0",
want: true,
wantErr: false,
},
{
name: "Peer version less than min version",
minVer: "1.0.0",
peerVer: "0.9.9",
want: false,
wantErr: false,
},
{
name: "Peer version with pre-release tag greater than min version",
minVer: "1.0.0",
peerVer: "1.0.1-alpha",
want: true,
wantErr: false,
},
{
name: "Invalid peer version format",
minVer: "1.0.0",
peerVer: "dev",
want: false,
wantErr: true,
},
{
name: "Invalid min version format",
minVer: "invalid.version",
peerVer: "1.0.0",
want: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := MeetsMinVersion(tt.minVer, tt.peerVer)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, got)
})
}
}
+1 -1
View File
@@ -106,7 +106,7 @@ func initTestPostureChecksAccount(am *DefaultAccountManager) (*types.Account, er
Role: types.UserRoleUser, Role: types.UserRoleUser,
} }
account := newAccountWithId(context.Background(), accountID, groupAdminUserID, domain) account := newAccountWithId(context.Background(), accountID, groupAdminUserID, domain, false)
account.Users[admin.Id] = admin account.Users[admin.Id] = admin
account.Users[user.Id] = user account.Users[user.Id] = user
+2 -2
View File
@@ -1284,7 +1284,7 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
permissionsManager := permissions.NewManager(store) permissionsManager := permissions.NewManager(store)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager) return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false)
} }
func createRouterStore(t *testing.T) (store.Store, error) { func createRouterStore(t *testing.T) (store.Store, error) {
@@ -1305,7 +1305,7 @@ func initTestRouteAccount(t *testing.T, am *DefaultAccountManager) (*types.Accou
accountID := "testingAcc" accountID := "testingAcc"
domain := "example.com" domain := "example.com"
account := newAccountWithId(context.Background(), accountID, userID, domain) account := newAccountWithId(context.Background(), accountID, userID, domain, false)
err := am.Store.SaveAccount(context.Background(), account) err := am.Store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
return nil, err return nil, err
+1 -1
View File
@@ -1184,7 +1184,7 @@ func NewSqliteStoreFromFileStore(ctx context.Context, fileStore *FileStore, data
for _, account := range fileStore.GetAllAccounts(ctx) { for _, account := range fileStore.GetAllAccounts(ctx) {
_, err = account.GetGroupAll() _, err = account.GetGroupAll()
if err != nil { if err != nil {
if err := account.AddAllGroup(); err != nil { if err := account.AddAllGroup(false); err != nil {
return nil, err return nil, err
} }
} }
+1 -1
View File
@@ -2044,7 +2044,7 @@ func newAccountWithId(ctx context.Context, accountID, userID, domain string) *ty
}, },
} }
if err := acc.AddAllGroup(); err != nil { if err := acc.AddAllGroup(false); err != nil {
log.WithContext(ctx).Errorf("error adding all group to account %s: %v", acc.Id, err) log.WithContext(ctx).Errorf("error adding all group to account %s: %v", acc.Id, err)
} }
return acc return acc
+1 -1
View File
@@ -391,7 +391,7 @@ func addAllGroupToAccount(ctx context.Context, store Store) error {
_, err := account.GetGroupAll() _, err := account.GetGroupAll()
if err != nil { if err != nil {
if err := account.AddAllGroup(); err != nil { if err := account.AddAllGroup(false); err != nil {
return err return err
} }
shouldSave = true shouldSave = true
@@ -18,6 +18,10 @@ type UpdateChannelMetrics struct {
getAllConnectedPeersDurationMicro metric.Int64Histogram getAllConnectedPeersDurationMicro metric.Int64Histogram
getAllConnectedPeers metric.Int64Histogram getAllConnectedPeers metric.Int64Histogram
hasChannelDurationMicro metric.Int64Histogram hasChannelDurationMicro metric.Int64Histogram
calcPostureChecksDurationMicro metric.Int64Histogram
calcPeerNetworkMapDurationMs metric.Int64Histogram
mergeNetworkMapDurationMicro metric.Int64Histogram
toSyncResponseDurationMicro metric.Int64Histogram
ctx context.Context ctx context.Context
} }
@@ -89,6 +93,38 @@ func NewUpdateChannelMetrics(ctx context.Context, meter metric.Meter) (*UpdateCh
return nil, err return nil, err
} }
calcPostureChecksDurationMicro, err := meter.Int64Histogram("management.updatechannel.calc.posturechecks.duration.micro",
metric.WithUnit("microseconds"),
metric.WithDescription("Duration of how long it takes to get the posture checks for a peer"),
)
if err != nil {
return nil, err
}
calcPeerNetworkMapDurationMs, err := meter.Int64Histogram("management.updatechannel.calc.networkmap.duration.ms",
metric.WithUnit("milliseconds"),
metric.WithDescription("Duration of how long it takes to calculate the network map for a peer"),
)
if err != nil {
return nil, err
}
mergeNetworkMapDurationMicro, err := meter.Int64Histogram("management.updatechannel.merge.networkmap.duration.micro",
metric.WithUnit("microseconds"),
metric.WithDescription("Duration of how long it takes to merge the network maps for a peer"),
)
if err != nil {
return nil, err
}
toSyncResponseDurationMicro, err := meter.Int64Histogram("management.updatechannel.tosyncresponse.duration.micro",
metric.WithUnit("microseconds"),
metric.WithDescription("Duration of how long it takes to convert the network map to sync response"),
)
if err != nil {
return nil, err
}
return &UpdateChannelMetrics{ return &UpdateChannelMetrics{
createChannelDurationMicro: createChannelDurationMicro, createChannelDurationMicro: createChannelDurationMicro,
closeChannelDurationMicro: closeChannelDurationMicro, closeChannelDurationMicro: closeChannelDurationMicro,
@@ -98,6 +134,10 @@ func NewUpdateChannelMetrics(ctx context.Context, meter metric.Meter) (*UpdateCh
getAllConnectedPeersDurationMicro: getAllConnectedPeersDurationMicro, getAllConnectedPeersDurationMicro: getAllConnectedPeersDurationMicro,
getAllConnectedPeers: getAllConnectedPeers, getAllConnectedPeers: getAllConnectedPeers,
hasChannelDurationMicro: hasChannelDurationMicro, hasChannelDurationMicro: hasChannelDurationMicro,
calcPostureChecksDurationMicro: calcPostureChecksDurationMicro,
calcPeerNetworkMapDurationMs: calcPeerNetworkMapDurationMs,
mergeNetworkMapDurationMicro: mergeNetworkMapDurationMicro,
toSyncResponseDurationMicro: toSyncResponseDurationMicro,
ctx: ctx, ctx: ctx,
}, nil }, nil
} }
@@ -137,3 +177,19 @@ func (metrics *UpdateChannelMetrics) CountGetAllConnectedPeersDuration(duration
func (metrics *UpdateChannelMetrics) CountHasChannelDuration(duration time.Duration) { func (metrics *UpdateChannelMetrics) CountHasChannelDuration(duration time.Duration) {
metrics.hasChannelDurationMicro.Record(metrics.ctx, duration.Microseconds()) metrics.hasChannelDurationMicro.Record(metrics.ctx, duration.Microseconds())
} }
func (metrics *UpdateChannelMetrics) CountCalcPostureChecksDuration(duration time.Duration) {
metrics.calcPostureChecksDurationMicro.Record(metrics.ctx, duration.Microseconds())
}
func (metrics *UpdateChannelMetrics) CountCalcPeerNetworkMapDuration(duration time.Duration) {
metrics.calcPeerNetworkMapDurationMs.Record(metrics.ctx, duration.Milliseconds())
}
func (metrics *UpdateChannelMetrics) CountMergeNetworkMapDuration(duration time.Duration) {
metrics.mergeNetworkMapDurationMicro.Record(metrics.ctx, duration.Microseconds())
}
func (metrics *UpdateChannelMetrics) CountToSyncResponseDuration(duration time.Duration) {
metrics.toSyncResponseDurationMicro.Record(metrics.ctx, duration.Microseconds())
}
+58 -18
View File
@@ -36,6 +36,9 @@ const (
PublicCategory = "public" PublicCategory = "public"
PrivateCategory = "private" PrivateCategory = "private"
UnknownCategory = "unknown" UnknownCategory = "unknown"
// firewallRuleMinPortRangesVer defines the minimum peer version that supports port range rules.
firewallRuleMinPortRangesVer = "0.48.0"
) )
type LookupMap map[string]struct{} type LookupMap map[string]struct{}
@@ -248,7 +251,7 @@ func (a *Account) GetPeerNetworkMap(
} }
} }
aclPeers, firewallRules := a.GetPeerConnectionResources(ctx, peerID, validatedPeersMap) aclPeers, firewallRules := a.GetPeerConnectionResources(ctx, peer, validatedPeersMap)
// exclude expired peers // exclude expired peers
var peersToConnect []*nbpeer.Peer var peersToConnect []*nbpeer.Peer
var expiredPeers []*nbpeer.Peer var expiredPeers []*nbpeer.Peer
@@ -961,8 +964,9 @@ func (a *Account) UserGroupsRemoveFromPeers(userID string, groups ...string) map
// GetPeerConnectionResources for a given peer // GetPeerConnectionResources for a given peer
// //
// This function returns the list of peers and firewall rules that are applicable to a given peer. // This function returns the list of peers and firewall rules that are applicable to a given peer.
func (a *Account) GetPeerConnectionResources(ctx context.Context, peerID string, validatedPeersMap map[string]struct{}) ([]*nbpeer.Peer, []*FirewallRule) { func (a *Account) GetPeerConnectionResources(ctx context.Context, peer *nbpeer.Peer, validatedPeersMap map[string]struct{}) ([]*nbpeer.Peer, []*FirewallRule) {
generateResources, getAccumulatedResources := a.connResourcesGenerator(ctx) generateResources, getAccumulatedResources := a.connResourcesGenerator(ctx, peer)
for _, policy := range a.Policies { for _, policy := range a.Policies {
if !policy.Enabled { if !policy.Enabled {
continue continue
@@ -973,8 +977,8 @@ func (a *Account) GetPeerConnectionResources(ctx context.Context, peerID string,
continue continue
} }
sourcePeers, peerInSources := a.getAllPeersFromGroups(ctx, rule.Sources, peerID, policy.SourcePostureChecks, validatedPeersMap) sourcePeers, peerInSources := a.getAllPeersFromGroups(ctx, rule.Sources, peer.ID, policy.SourcePostureChecks, validatedPeersMap)
destinationPeers, peerInDestinations := a.getAllPeersFromGroups(ctx, rule.Destinations, peerID, nil, validatedPeersMap) destinationPeers, peerInDestinations := a.getAllPeersFromGroups(ctx, rule.Destinations, peer.ID, nil, validatedPeersMap)
if rule.Bidirectional { if rule.Bidirectional {
if peerInSources { if peerInSources {
@@ -1003,7 +1007,7 @@ func (a *Account) GetPeerConnectionResources(ctx context.Context, peerID string,
// The generator function is used to generate the list of peers and firewall rules that are applicable to a given peer. // The generator function is used to generate the list of peers and firewall rules that are applicable to a given peer.
// It safe to call the generator function multiple times for same peer and different rules no duplicates will be // It safe to call the generator function multiple times for same peer and different rules no duplicates will be
// generated. The accumulator function returns the result of all the generator calls. // generated. The accumulator function returns the result of all the generator calls.
func (a *Account) connResourcesGenerator(ctx context.Context) (func(*PolicyRule, []*nbpeer.Peer, int), func() ([]*nbpeer.Peer, []*FirewallRule)) { func (a *Account) connResourcesGenerator(ctx context.Context, targetPeer *nbpeer.Peer) (func(*PolicyRule, []*nbpeer.Peer, int), func() ([]*nbpeer.Peer, []*FirewallRule)) {
rulesExists := make(map[string]struct{}) rulesExists := make(map[string]struct{})
peersExists := make(map[string]struct{}) peersExists := make(map[string]struct{})
rules := make([]*FirewallRule, 0) rules := make([]*FirewallRule, 0)
@@ -1051,17 +1055,7 @@ func (a *Account) connResourcesGenerator(ctx context.Context) (func(*PolicyRule,
continue continue
} }
for _, port := range rule.Ports { rules = append(rules, expandPortsAndRanges(fr, rule, targetPeer)...)
pr := fr // clone rule and add set new port
pr.Port = port
rules = append(rules, &pr)
}
for _, portRange := range rule.PortRanges {
pr := fr
pr.PortRange = portRange
rules = append(rules, &pr)
}
} }
}, func() ([]*nbpeer.Peer, []*FirewallRule) { }, func() ([]*nbpeer.Peer, []*FirewallRule) {
return peers, rules return peers, rules
@@ -1552,7 +1546,7 @@ func getPoliciesSourcePeers(policies []*Policy, groups map[string]*Group) map[st
} }
// AddAllGroup to account object if it doesn't exist // AddAllGroup to account object if it doesn't exist
func (a *Account) AddAllGroup() error { func (a *Account) AddAllGroup(disableDefaultPolicy bool) error {
if len(a.Groups) == 0 { if len(a.Groups) == 0 {
allGroup := &Group{ allGroup := &Group{
ID: xid.New().String(), ID: xid.New().String(),
@@ -1564,6 +1558,10 @@ func (a *Account) AddAllGroup() error {
} }
a.Groups = map[string]*Group{allGroup.ID: allGroup} a.Groups = map[string]*Group{allGroup.ID: allGroup}
if disableDefaultPolicy {
return nil
}
id := xid.New().String() id := xid.New().String()
defaultPolicy := &Policy{ defaultPolicy := &Policy{
@@ -1590,3 +1588,45 @@ func (a *Account) AddAllGroup() error {
} }
return nil return nil
} }
// expandPortsAndRanges expands Ports and PortRanges of a rule into individual firewall rules
func expandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *nbpeer.Peer) []*FirewallRule {
var expanded []*FirewallRule
if len(rule.Ports) > 0 {
for _, port := range rule.Ports {
fr := base
fr.Port = port
expanded = append(expanded, &fr)
}
return expanded
}
supportPortRanges := peerSupportsPortRanges(peer.Meta.WtVersion)
for _, portRange := range rule.PortRanges {
fr := base
if supportPortRanges {
fr.PortRange = portRange
} else {
// Peer doesn't support port ranges, only allow single-port ranges
if portRange.Start != portRange.End {
continue
}
fr.Port = strconv.FormatUint(uint64(portRange.Start), 10)
}
expanded = append(expanded, &fr)
}
return expanded
}
// peerSupportsPortRanges checks if the peer version supports port ranges.
func peerSupportsPortRanges(peerVer string) bool {
if strings.Contains(peerVer, "dev") {
return true
}
meetMinVer, err := posture.MeetsMinVersion(firewallRuleMinPortRangesVer, peerVer)
return err == nil && meetMinVer
}
+3
View File
@@ -53,6 +53,9 @@ type Config struct {
StoreConfig StoreConfig StoreConfig StoreConfig
ReverseProxy ReverseProxy ReverseProxy ReverseProxy
// disable default all-to-all policy
DisableDefaultPolicy bool
} }
// GetAuthAudiences returns the audience from the http config and device authorization flow config // GetAuthAudiences returns the audience from the http config and device authorization flow config
-1
View File
@@ -76,7 +76,6 @@ func generateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule
rules = append(rules, generateRulesWithPortRanges(baseRule, rule, rulesExists)...) rules = append(rules, generateRulesWithPortRanges(baseRule, rule, rulesExists)...)
} else { } else {
rules = append(rules, generateRulesWithPorts(ctx, baseRule, rule, rulesExists)...) rules = append(rules, generateRulesWithPorts(ctx, baseRule, rule, rulesExists)...)
} }
// TODO: generate IPv6 rules for dynamic routes // TODO: generate IPv6 rules for dynamic routes
+25 -25
View File
@@ -56,7 +56,7 @@ func TestUser_CreatePAT_ForSameUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = s.SaveAccount(context.Background(), account) err = s.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -103,7 +103,7 @@ func TestUser_CreatePAT_ForDifferentUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users[mockTargetUserId] = &types.User{ account.Users[mockTargetUserId] = &types.User{
Id: mockTargetUserId, Id: mockTargetUserId,
IsServiceUser: false, IsServiceUser: false,
@@ -131,7 +131,7 @@ func TestUser_CreatePAT_ForServiceUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users[mockTargetUserId] = &types.User{ account.Users[mockTargetUserId] = &types.User{
Id: mockTargetUserId, Id: mockTargetUserId,
IsServiceUser: true, IsServiceUser: true,
@@ -163,7 +163,7 @@ func TestUser_CreatePAT_WithWrongExpiration(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -188,7 +188,7 @@ func TestUser_CreatePAT_WithEmptyName(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -213,7 +213,7 @@ func TestUser_DeletePAT(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users[mockUserID] = &types.User{ account.Users[mockUserID] = &types.User{
Id: mockUserID, Id: mockUserID,
PATs: map[string]*types.PersonalAccessToken{ PATs: map[string]*types.PersonalAccessToken{
@@ -256,7 +256,7 @@ func TestUser_GetPAT(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users[mockUserID] = &types.User{ account.Users[mockUserID] = &types.User{
Id: mockUserID, Id: mockUserID,
AccountID: mockAccountID, AccountID: mockAccountID,
@@ -296,7 +296,7 @@ func TestUser_GetAllPATs(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users[mockUserID] = &types.User{ account.Users[mockUserID] = &types.User{
Id: mockUserID, Id: mockUserID,
AccountID: mockAccountID, AccountID: mockAccountID,
@@ -406,7 +406,7 @@ func TestUser_CreateServiceUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -453,7 +453,7 @@ func TestUser_CreateUser_ServiceUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -501,7 +501,7 @@ func TestUser_CreateUser_RegularUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -532,7 +532,7 @@ func TestUser_InviteNewUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -639,7 +639,7 @@ func TestUser_DeleteUser_ServiceUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users[mockServiceUserID] = tt.serviceUser account.Users[mockServiceUserID] = tt.serviceUser
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
@@ -678,7 +678,7 @@ func TestUser_DeleteUser_SelfDelete(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -705,7 +705,7 @@ func TestUser_DeleteUser_regularUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
targetId := "user2" targetId := "user2"
account.Users[targetId] = &types.User{ account.Users[targetId] = &types.User{
@@ -792,7 +792,7 @@ func TestUser_DeleteUser_RegularUsers(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
targetId := "user2" targetId := "user2"
account.Users[targetId] = &types.User{ account.Users[targetId] = &types.User{
@@ -952,7 +952,7 @@ func TestDefaultAccountManager_GetUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
err = store.SaveAccount(context.Background(), account) err = store.SaveAccount(context.Background(), account)
if err != nil { if err != nil {
@@ -988,7 +988,7 @@ func TestDefaultAccountManager_ListUsers(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users["normal_user1"] = types.NewRegularUser("normal_user1") account.Users["normal_user1"] = types.NewRegularUser("normal_user1")
account.Users["normal_user2"] = types.NewRegularUser("normal_user2") account.Users["normal_user2"] = types.NewRegularUser("normal_user2")
@@ -1030,7 +1030,7 @@ func TestDefaultAccountManager_ExternalCache(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
externalUser := &types.User{ externalUser := &types.User{
Id: "externalUser", Id: "externalUser",
Role: types.UserRoleUser, Role: types.UserRoleUser,
@@ -1098,7 +1098,7 @@ func TestUser_GetUsersFromAccount_ForAdmin(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users[mockServiceUserID] = &types.User{ account.Users[mockServiceUserID] = &types.User{
Id: mockServiceUserID, Id: mockServiceUserID,
Role: "user", Role: "user",
@@ -1132,7 +1132,7 @@ func TestUser_GetUsersFromAccount_ForUser(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "") account := newAccountWithId(context.Background(), mockAccountID, mockUserID, "", false)
account.Users[mockServiceUserID] = &types.User{ account.Users[mockServiceUserID] = &types.User{
Id: mockServiceUserID, Id: mockServiceUserID,
Role: "user", Role: "user",
@@ -1499,7 +1499,7 @@ func TestSaveOrAddUser_PreventAccountSwitch(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account1 := newAccountWithId(context.Background(), "account1", "ownerAccount1", "") account1 := newAccountWithId(context.Background(), "account1", "ownerAccount1", "", false)
targetId := "user2" targetId := "user2"
account1.Users[targetId] = &types.User{ account1.Users[targetId] = &types.User{
Id: targetId, Id: targetId,
@@ -1508,7 +1508,7 @@ func TestSaveOrAddUser_PreventAccountSwitch(t *testing.T) {
} }
require.NoError(t, s.SaveAccount(context.Background(), account1)) require.NoError(t, s.SaveAccount(context.Background(), account1))
account2 := newAccountWithId(context.Background(), "account2", "ownerAccount2", "") account2 := newAccountWithId(context.Background(), "account2", "ownerAccount2", "", false)
require.NoError(t, s.SaveAccount(context.Background(), account2)) require.NoError(t, s.SaveAccount(context.Background(), account2))
permissionsManager := permissions.NewManager(s) permissionsManager := permissions.NewManager(s)
@@ -1535,7 +1535,7 @@ func TestDefaultAccountManager_GetCurrentUserInfo(t *testing.T) {
} }
t.Cleanup(cleanup) t.Cleanup(cleanup)
account1 := newAccountWithId(context.Background(), "account1", "account1Owner", "") account1 := newAccountWithId(context.Background(), "account1", "account1Owner", "", false)
account1.Settings.RegularUsersViewBlocked = false account1.Settings.RegularUsersViewBlocked = false
account1.Users["blocked-user"] = &types.User{ account1.Users["blocked-user"] = &types.User{
Id: "blocked-user", Id: "blocked-user",
@@ -1557,7 +1557,7 @@ func TestDefaultAccountManager_GetCurrentUserInfo(t *testing.T) {
} }
require.NoError(t, store.SaveAccount(context.Background(), account1)) require.NoError(t, store.SaveAccount(context.Background(), account1))
account2 := newAccountWithId(context.Background(), "account2", "account2Owner", "") account2 := newAccountWithId(context.Background(), "account2", "account2Owner", "", false)
account2.Users["settings-blocked-user"] = &types.User{ account2.Users["settings-blocked-user"] = &types.User{
Id: "settings-blocked-user", Id: "settings-blocked-user",
Role: types.UserRoleUser, Role: types.UserRoleUser,
+9 -5
View File
@@ -130,7 +130,7 @@ repo_gpgcheck=1
EOF EOF
} }
add_aur_repo() { install_aur_package() {
INSTALL_PKGS="git base-devel go" INSTALL_PKGS="git base-devel go"
REMOVE_PKGS="" REMOVE_PKGS=""
@@ -154,8 +154,10 @@ add_aur_repo() {
cd netbird-ui && makepkg -sri --noconfirm cd netbird-ui && makepkg -sri --noconfirm
fi fi
# Clean up the installed packages if [ -n "$REMOVE_PKGS" ]; then
${SUDO} pacman -Rs "$REMOVE_PKGS" --noconfirm # Clean up the installed packages
${SUDO} pacman -Rs "$REMOVE_PKGS" --noconfirm
fi
} }
prepare_tun_module() { prepare_tun_module() {
@@ -277,7 +279,9 @@ install_netbird() {
;; ;;
pacman) pacman)
${SUDO} pacman -Syy ${SUDO} pacman -Syy
add_aur_repo install_aur_package
# in-line with the docs at https://wiki.archlinux.org/title/Netbird
${SUDO} systemctl enable --now netbird@main.service
;; ;;
pkg) pkg)
# Check if the package is already installed # Check if the package is already installed
@@ -494,4 +498,4 @@ case "$UPDATE_FLAG" in
;; ;;
*) *)
install_netbird install_netbird
esac esac
+12 -2
View File
@@ -21,6 +21,7 @@ var (
// Update fetch the version info periodically and notify the onUpdateListener in case the UI version or the // Update fetch the version info periodically and notify the onUpdateListener in case the UI version or the
// daemon version are deprecated // daemon version are deprecated
type Update struct { type Update struct {
httpAgent string
uiVersion *goversion.Version uiVersion *goversion.Version
daemonVersion *goversion.Version daemonVersion *goversion.Version
latestAvailable *goversion.Version latestAvailable *goversion.Version
@@ -34,7 +35,7 @@ type Update struct {
} }
// NewUpdate instantiate Update and start to fetch the new version information // NewUpdate instantiate Update and start to fetch the new version information
func NewUpdate() *Update { func NewUpdate(httpAgent string) *Update {
currentVersion, err := goversion.NewVersion(version) currentVersion, err := goversion.NewVersion(version)
if err != nil { if err != nil {
currentVersion, _ = goversion.NewVersion("0.0.0") currentVersion, _ = goversion.NewVersion("0.0.0")
@@ -43,6 +44,7 @@ func NewUpdate() *Update {
latestAvailable, _ := goversion.NewVersion("0.0.0") latestAvailable, _ := goversion.NewVersion("0.0.0")
u := &Update{ u := &Update{
httpAgent: httpAgent,
latestAvailable: latestAvailable, latestAvailable: latestAvailable,
uiVersion: currentVersion, uiVersion: currentVersion,
fetchTicker: time.NewTicker(fetchPeriod), fetchTicker: time.NewTicker(fetchPeriod),
@@ -112,7 +114,15 @@ func (u *Update) startFetcher() {
func (u *Update) fetchVersion() bool { func (u *Update) fetchVersion() bool {
log.Debugf("fetching version info from %s", versionURL) log.Debugf("fetching version info from %s", versionURL)
resp, err := http.Get(versionURL) req, err := http.NewRequest("GET", versionURL, nil)
if err != nil {
log.Errorf("failed to create request for version info: %s", err)
return false
}
req.Header.Set("User-Agent", u.httpAgent)
resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
log.Errorf("failed to fetch version info: %s", err) log.Errorf("failed to fetch version info: %s", err)
return false return false
+5 -3
View File
@@ -9,6 +9,8 @@ import (
"time" "time"
) )
const httpAgent = "pkg/test"
func TestNewUpdate(t *testing.T) { func TestNewUpdate(t *testing.T) {
version = "1.0.0" version = "1.0.0"
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -21,7 +23,7 @@ func TestNewUpdate(t *testing.T) {
wg.Add(1) wg.Add(1)
onUpdate := false onUpdate := false
u := NewUpdate() u := NewUpdate(httpAgent)
defer u.StopWatch() defer u.StopWatch()
u.SetOnUpdateListener(func() { u.SetOnUpdateListener(func() {
onUpdate = true onUpdate = true
@@ -46,7 +48,7 @@ func TestDoNotUpdate(t *testing.T) {
wg.Add(1) wg.Add(1)
onUpdate := false onUpdate := false
u := NewUpdate() u := NewUpdate(httpAgent)
defer u.StopWatch() defer u.StopWatch()
u.SetOnUpdateListener(func() { u.SetOnUpdateListener(func() {
onUpdate = true onUpdate = true
@@ -71,7 +73,7 @@ func TestDaemonUpdate(t *testing.T) {
wg.Add(1) wg.Add(1)
onUpdate := false onUpdate := false
u := NewUpdate() u := NewUpdate(httpAgent)
defer u.StopWatch() defer u.StopWatch()
u.SetOnUpdateListener(func() { u.SetOnUpdateListener(func() {
onUpdate = true onUpdate = true