mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-29 13:46:41 +00:00
Compare commits
10 Commits
test/multi
...
v0.49.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d897365abc | ||
|
|
f37aa2cc9d | ||
|
|
5343bee7b2 | ||
|
|
870e29db63 | ||
|
|
08e9b05d51 | ||
|
|
3581648071 | ||
|
|
2a51609436 | ||
|
|
83457f8b99 | ||
|
|
b45284f086 | ||
|
|
e9016aecea |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -9,7 +9,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SIGN_PIPE_VER: "v0.0.18"
|
SIGN_PIPE_VER: "v0.0.19"
|
||||||
GORELEASER_VER: "v2.3.2"
|
GORELEASER_VER: "v2.3.2"
|
||||||
PRODUCT_NAME: "NetBird"
|
PRODUCT_NAME: "NetBird"
|
||||||
COPYRIGHT: "NetBird GmbH"
|
COPYRIGHT: "NetBird GmbH"
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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")
|
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.")
|
||||||
|
|||||||
@@ -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),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1853,40 +1853,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"])
|
||||||
|
|||||||
@@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1590,3 +1584,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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user