mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-31 13:09:55 +00:00
Compare commits
3 Commits
fix-ssh-au
...
v0.71.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f91f49277 | ||
|
|
347c5bf317 | ||
|
|
22e2519d71 |
@@ -260,15 +260,23 @@ WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}"
|
|||||||
|
|
||||||
WriteRegStr ${REG_ROOT} "${UI_REG_APP_PATH}" "" "$INSTDIR\${UI_APP_EXE}"
|
WriteRegStr ${REG_ROOT} "${UI_REG_APP_PATH}" "" "$INSTDIR\${UI_APP_EXE}"
|
||||||
|
|
||||||
; Create autostart registry entry based on checkbox
|
; Drop Run, App Paths and Uninstall entries left in the 32-bit registry view
|
||||||
|
; or HKCU by legacy installers.
|
||||||
|
DetailPrint "Cleaning legacy 32-bit / HKCU entries..."
|
||||||
|
DeleteRegValue HKCU "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
||||||
|
SetRegView 32
|
||||||
|
DeleteRegValue HKLM "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
||||||
|
DeleteRegKey HKLM "${REG_APP_PATH}"
|
||||||
|
DeleteRegKey HKLM "${UI_REG_APP_PATH}"
|
||||||
|
DeleteRegKey HKLM "${UNINSTALL_PATH}"
|
||||||
|
SetRegView 64
|
||||||
|
|
||||||
DetailPrint "Autostart enabled: $AutostartEnabled"
|
DetailPrint "Autostart enabled: $AutostartEnabled"
|
||||||
${If} $AutostartEnabled == "1"
|
${If} $AutostartEnabled == "1"
|
||||||
WriteRegStr HKLM "${AUTOSTART_REG_KEY}" "${APP_NAME}" '"$INSTDIR\${UI_APP_EXE}.exe"'
|
WriteRegStr HKLM "${AUTOSTART_REG_KEY}" "${APP_NAME}" '"$INSTDIR\${UI_APP_EXE}.exe"'
|
||||||
DetailPrint "Added autostart registry entry: $INSTDIR\${UI_APP_EXE}.exe"
|
DetailPrint "Added autostart registry entry: $INSTDIR\${UI_APP_EXE}.exe"
|
||||||
${Else}
|
${Else}
|
||||||
DeleteRegValue HKLM "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
DeleteRegValue HKLM "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
||||||
; Legacy: pre-HKLM installs wrote to HKCU; clean that up too.
|
|
||||||
DeleteRegValue HKCU "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
|
||||||
DetailPrint "Autostart not enabled by user"
|
DetailPrint "Autostart not enabled by user"
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
@@ -299,11 +307,16 @@ ExecWait '"$INSTDIR\${MAIN_APP_EXE}" service uninstall'
|
|||||||
DetailPrint "Terminating Netbird UI process..."
|
DetailPrint "Terminating Netbird UI process..."
|
||||||
ExecWait `taskkill /im ${UI_APP_EXE}.exe /f`
|
ExecWait `taskkill /im ${UI_APP_EXE}.exe /f`
|
||||||
|
|
||||||
; Remove autostart registry entry
|
; Remove autostart entries from every view a previous installer may have used.
|
||||||
DetailPrint "Removing autostart registry entry if exists..."
|
DetailPrint "Removing autostart registry entry if exists..."
|
||||||
DeleteRegValue HKLM "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
DeleteRegValue HKLM "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
||||||
; Legacy: pre-HKLM installs wrote to HKCU; clean that up too.
|
|
||||||
DeleteRegValue HKCU "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
DeleteRegValue HKCU "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
||||||
|
SetRegView 32
|
||||||
|
DeleteRegValue HKLM "${AUTOSTART_REG_KEY}" "${APP_NAME}"
|
||||||
|
DeleteRegKey HKLM "${REG_APP_PATH}"
|
||||||
|
DeleteRegKey HKLM "${UI_REG_APP_PATH}"
|
||||||
|
DeleteRegKey HKLM "${UNINSTALL_PATH}"
|
||||||
|
SetRegView 64
|
||||||
|
|
||||||
; Handle data deletion based on checkbox
|
; Handle data deletion based on checkbox
|
||||||
DetailPrint "Checking if user requested data deletion..."
|
DetailPrint "Checking if user requested data deletion..."
|
||||||
|
|||||||
@@ -64,6 +64,13 @@
|
|||||||
<RegistryValue Name="InstalledByMSI" Type="integer" Value="1" KeyPath="yes" />
|
<RegistryValue Name="InstalledByMSI" Type="integer" Value="1" KeyPath="yes" />
|
||||||
</RegistryKey>
|
</RegistryKey>
|
||||||
</Component>
|
</Component>
|
||||||
|
<!-- Drop the HKCU Run\Netbird value written by legacy NSIS installers. -->
|
||||||
|
<Component Id="NetbirdLegacyHKCUCleanup" Guid="*">
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\NetBird GmbH\Installer"
|
||||||
|
Name="LegacyHKCUCleanup" Type="integer" Value="1" KeyPath="yes" />
|
||||||
|
<RemoveRegistryValue Root="HKCU"
|
||||||
|
Key="Software\Microsoft\Windows\CurrentVersion\Run" Name="Netbird" />
|
||||||
|
</Component>
|
||||||
</StandardDirectory>
|
</StandardDirectory>
|
||||||
|
|
||||||
<StandardDirectory Id="CommonAppDataFolder">
|
<StandardDirectory Id="CommonAppDataFolder">
|
||||||
@@ -76,10 +83,28 @@
|
|||||||
</Directory>
|
</Directory>
|
||||||
</StandardDirectory>
|
</StandardDirectory>
|
||||||
|
|
||||||
|
<!-- Drop Run, App Paths and Uninstall entries written by legacy NSIS
|
||||||
|
installers into the 32-bit registry view (HKLM\Software\Wow6432Node). -->
|
||||||
|
<Component Id="NetbirdLegacyWow6432Cleanup" Directory="NetbirdInstallDir"
|
||||||
|
Guid="bda5d628-16bd-4086-b2c1-5099d8d51763" Bitness="always32">
|
||||||
|
<RegistryValue Root="HKLM" Key="Software\NetBird GmbH\Installer"
|
||||||
|
Name="LegacyWow6432Cleanup" Type="integer" Value="1" KeyPath="yes" />
|
||||||
|
<RemoveRegistryValue Root="HKLM"
|
||||||
|
Key="Software\Microsoft\Windows\CurrentVersion\Run" Name="Netbird" />
|
||||||
|
<RemoveRegistryKey Action="removeOnInstall" Root="HKLM"
|
||||||
|
Key="Software\Microsoft\Windows\CurrentVersion\App Paths\Netbird" />
|
||||||
|
<RemoveRegistryKey Action="removeOnInstall" Root="HKLM"
|
||||||
|
Key="Software\Microsoft\Windows\CurrentVersion\App Paths\Netbird-ui" />
|
||||||
|
<RemoveRegistryKey Action="removeOnInstall" Root="HKLM"
|
||||||
|
Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\Netbird" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
<ComponentGroup Id="NetbirdFilesComponent">
|
<ComponentGroup Id="NetbirdFilesComponent">
|
||||||
<ComponentRef Id="NetbirdFiles" />
|
<ComponentRef Id="NetbirdFiles" />
|
||||||
<ComponentRef Id="NetbirdAumidRegistry" />
|
<ComponentRef Id="NetbirdAumidRegistry" />
|
||||||
<ComponentRef Id="NetbirdAutoStart" />
|
<ComponentRef Id="NetbirdAutoStart" />
|
||||||
|
<ComponentRef Id="NetbirdLegacyHKCUCleanup" />
|
||||||
|
<ComponentRef Id="NetbirdLegacyWow6432Cleanup" />
|
||||||
</ComponentGroup>
|
</ComponentGroup>
|
||||||
|
|
||||||
<util:CloseApplication Id="CloseNetBird" CloseMessage="no" Target="netbird.exe" RebootPrompt="no" />
|
<util:CloseApplication Id="CloseNetBird" CloseMessage="no" Target="netbird.exe" RebootPrompt="no" />
|
||||||
|
|||||||
@@ -522,10 +522,11 @@ func (s *Server) sendJob(ctx context.Context, peerKey wgtypes.Key, job *job.Even
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) cancelPeerRoutines(ctx context.Context, accountID string, peer *nbpeer.Peer, streamStartTime time.Time) {
|
func (s *Server) cancelPeerRoutines(ctx context.Context, accountID string, peer *nbpeer.Peer, streamStartTime time.Time) {
|
||||||
unlock := s.acquirePeerLockByUID(ctx, peer.Key)
|
uncanceledCTX := context.WithoutCancel(ctx)
|
||||||
|
unlock := s.acquirePeerLockByUID(uncanceledCTX, peer.Key)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
s.cancelPeerRoutinesWithoutLock(ctx, accountID, peer, streamStartTime)
|
s.cancelPeerRoutinesWithoutLock(uncanceledCTX, accountID, peer, streamStartTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) cancelPeerRoutinesWithoutLock(ctx context.Context, accountID string, peer *nbpeer.Peer, streamStartTime time.Time) {
|
func (s *Server) cancelPeerRoutinesWithoutLock(ctx context.Context, accountID string, peer *nbpeer.Peer, streamStartTime time.Time) {
|
||||||
|
|||||||
@@ -291,10 +291,15 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
|||||||
return nil, status.NewPermissionDeniedError()
|
return nil, status.NewPermissionDeniedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Canonicalize the incoming range so a caller-supplied prefix with host bits
|
||||||
|
// (e.g. 100.64.1.1/16) compares equal to the masked form stored on network.Net.
|
||||||
|
newSettings.NetworkRange = newSettings.NetworkRange.Masked()
|
||||||
|
|
||||||
var oldSettings *types.Settings
|
var oldSettings *types.Settings
|
||||||
var updateAccountPeers bool
|
var updateAccountPeers bool
|
||||||
var groupChangesAffectPeers bool
|
var groupChangesAffectPeers bool
|
||||||
var reloadReverseProxy bool
|
var reloadReverseProxy bool
|
||||||
|
var effectiveOldNetworkRange netip.Prefix
|
||||||
|
|
||||||
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||||
var groupsUpdated bool
|
var groupsUpdated bool
|
||||||
@@ -308,6 +313,16 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No lock: the transaction already holds Settings(Update), and network.Net is
|
||||||
|
// only mutated by reallocateAccountPeerIPs, which is reachable only through
|
||||||
|
// this same code path. A Share lock here would extend an unnecessary row lock
|
||||||
|
// and complicate ordering against updatePeerIPv6InTransaction.
|
||||||
|
network, err := transaction.GetAccountNetwork(ctx, store.LockingStrengthNone, accountID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get account network: %w", err)
|
||||||
|
}
|
||||||
|
effectiveOldNetworkRange = prefixFromIPNet(network.Net)
|
||||||
|
|
||||||
if oldSettings.Extra != nil && newSettings.Extra != nil &&
|
if oldSettings.Extra != nil && newSettings.Extra != nil &&
|
||||||
oldSettings.Extra.PeerApprovalEnabled && !newSettings.Extra.PeerApprovalEnabled {
|
oldSettings.Extra.PeerApprovalEnabled && !newSettings.Extra.PeerApprovalEnabled {
|
||||||
approvedCount, err := transaction.ApproveAccountPeers(ctx, accountID)
|
approvedCount, err := transaction.ApproveAccountPeers(ctx, accountID)
|
||||||
@@ -321,7 +336,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldSettings.NetworkRange != newSettings.NetworkRange {
|
if newSettings.NetworkRange.IsValid() && newSettings.NetworkRange != effectiveOldNetworkRange {
|
||||||
if err = am.reallocateAccountPeerIPs(ctx, transaction, accountID, newSettings.NetworkRange); err != nil {
|
if err = am.reallocateAccountPeerIPs(ctx, transaction, accountID, newSettings.NetworkRange); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -396,9 +411,9 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
|||||||
}
|
}
|
||||||
am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountDNSDomainUpdated, eventMeta)
|
am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountDNSDomainUpdated, eventMeta)
|
||||||
}
|
}
|
||||||
if oldSettings.NetworkRange != newSettings.NetworkRange {
|
if newSettings.NetworkRange.IsValid() && newSettings.NetworkRange != effectiveOldNetworkRange {
|
||||||
eventMeta := map[string]any{
|
eventMeta := map[string]any{
|
||||||
"old_network_range": oldSettings.NetworkRange.String(),
|
"old_network_range": effectiveOldNetworkRange.String(),
|
||||||
"new_network_range": newSettings.NetworkRange.String(),
|
"new_network_range": newSettings.NetworkRange.String(),
|
||||||
}
|
}
|
||||||
am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountNetworkRangeUpdated, eventMeta)
|
am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountNetworkRangeUpdated, eventMeta)
|
||||||
@@ -443,6 +458,22 @@ func ipv6SettingsChanged(old, updated *types.Settings) bool {
|
|||||||
return !slices.Equal(oldGroups, newGroups)
|
return !slices.Equal(oldGroups, newGroups)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prefixFromIPNet returns the overlay prefix actually allocated on the account
|
||||||
|
// network, or an invalid prefix if none is set. Settings.NetworkRange is a
|
||||||
|
// user-facing override that is empty on legacy accounts, so the effective
|
||||||
|
// range must be read from network.Net to compare against an incoming update.
|
||||||
|
func prefixFromIPNet(ipNet net.IPNet) netip.Prefix {
|
||||||
|
if ipNet.IP == nil {
|
||||||
|
return netip.Prefix{}
|
||||||
|
}
|
||||||
|
addr, ok := netip.AddrFromSlice(ipNet.IP)
|
||||||
|
if !ok {
|
||||||
|
return netip.Prefix{}
|
||||||
|
}
|
||||||
|
ones, _ := ipNet.Mask.Size()
|
||||||
|
return netip.PrefixFrom(addr.Unmap(), ones)
|
||||||
|
}
|
||||||
|
|
||||||
func (am *DefaultAccountManager) validateSettingsUpdate(ctx context.Context, transaction store.Store, newSettings, oldSettings *types.Settings, userID, accountID string) error {
|
func (am *DefaultAccountManager) validateSettingsUpdate(ctx context.Context, transaction store.Store, newSettings, oldSettings *types.Settings, userID, accountID string) error {
|
||||||
halfYearLimit := 180 * 24 * time.Hour
|
halfYearLimit := 180 * 24 * time.Hour
|
||||||
if newSettings.PeerLoginExpiration > halfYearLimit {
|
if newSettings.PeerLoginExpiration > halfYearLimit {
|
||||||
|
|||||||
@@ -3970,6 +3970,96 @@ func TestDefaultAccountManager_UpdateAccountSettings_NetworkRangeChange(t *testi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDefaultAccountManager_UpdateAccountSettings_NetworkRangePreserved guards against
|
||||||
|
// peer IP reallocation when a settings update carries the network range that is already
|
||||||
|
// in use. Legacy accounts have Settings.NetworkRange unset in the DB while network.Net
|
||||||
|
// holds the actual allocated overlay; the dashboard backfills the GET response from
|
||||||
|
// network.Net and echoes the value back on PUT, so the diff must be against the
|
||||||
|
// effective range to avoid renumbering every peer on an unrelated settings change.
|
||||||
|
func TestDefaultAccountManager_UpdateAccountSettings_NetworkRangePreserved(t *testing.T) {
|
||||||
|
manager, _, account, peer1, peer2, peer3 := setupNetworkMapTest(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
settings, err := manager.Store.GetAccountSettings(ctx, store.LockingStrengthNone, account.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, settings.NetworkRange.IsValid(), "precondition: new accounts leave Settings.NetworkRange unset")
|
||||||
|
|
||||||
|
network, err := manager.Store.GetAccountNetwork(ctx, store.LockingStrengthNone, account.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, network.Net.IP, "precondition: network.Net should be allocated")
|
||||||
|
addr, ok := netip.AddrFromSlice(network.Net.IP)
|
||||||
|
require.True(t, ok)
|
||||||
|
ones, _ := network.Net.Mask.Size()
|
||||||
|
effective := netip.PrefixFrom(addr.Unmap(), ones)
|
||||||
|
require.True(t, effective.IsValid())
|
||||||
|
|
||||||
|
before := map[string]netip.Addr{peer1.ID: peer1.IP, peer2.ID: peer2.IP, peer3.ID: peer3.IP}
|
||||||
|
|
||||||
|
// Round-trip the effective range as if the dashboard echoed back the GET-backfilled value.
|
||||||
|
_, err = manager.UpdateAccountSettings(ctx, account.Id, userID, &types.Settings{
|
||||||
|
PeerLoginExpirationEnabled: true,
|
||||||
|
PeerLoginExpiration: types.DefaultPeerLoginExpiration,
|
||||||
|
NetworkRange: effective,
|
||||||
|
Extra: &types.ExtraSettings{},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
peers, err := manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, peers, len(before))
|
||||||
|
for _, p := range peers {
|
||||||
|
assert.Equal(t, before[p.ID], p.IP, "peer %s IP should not change when range matches effective", p.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carrying the same range with host bits set must also be a no-op once canonicalized.
|
||||||
|
hostBitsForm := netip.PrefixFrom(peer1.IP, ones)
|
||||||
|
require.NotEqual(t, effective, hostBitsForm, "precondition: host-bit form should differ before masking")
|
||||||
|
_, err = manager.UpdateAccountSettings(ctx, account.Id, userID, &types.Settings{
|
||||||
|
PeerLoginExpirationEnabled: true,
|
||||||
|
PeerLoginExpiration: types.DefaultPeerLoginExpiration,
|
||||||
|
NetworkRange: hostBitsForm,
|
||||||
|
Extra: &types.ExtraSettings{},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, p := range peers {
|
||||||
|
assert.Equal(t, before[p.ID], p.IP, "peer %s IP should not change for host-bit-set equivalent range", p.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Omitting NetworkRange (invalid prefix) must also be a no-op.
|
||||||
|
_, err = manager.UpdateAccountSettings(ctx, account.Id, userID, &types.Settings{
|
||||||
|
PeerLoginExpirationEnabled: true,
|
||||||
|
PeerLoginExpiration: types.DefaultPeerLoginExpiration,
|
||||||
|
Extra: &types.ExtraSettings{},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, p := range peers {
|
||||||
|
assert.Equal(t, before[p.ID], p.IP, "peer %s IP should not change when NetworkRange omitted", p.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity: an actually different range still triggers reallocation.
|
||||||
|
newRange := netip.MustParsePrefix("100.99.0.0/16")
|
||||||
|
_, err = manager.UpdateAccountSettings(ctx, account.Id, userID, &types.Settings{
|
||||||
|
PeerLoginExpirationEnabled: true,
|
||||||
|
PeerLoginExpiration: types.DefaultPeerLoginExpiration,
|
||||||
|
NetworkRange: newRange,
|
||||||
|
Extra: &types.ExtraSettings{},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
peers, err = manager.Store.GetAccountPeers(ctx, store.LockingStrengthNone, account.Id, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, p := range peers {
|
||||||
|
assert.True(t, newRange.Contains(p.IP), "peer %s should be in new range %s, got %s", p.ID, newRange, p.IP)
|
||||||
|
assert.NotEqual(t, before[p.ID], p.IP, "peer %s IP should change on real range update", p.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDefaultAccountManager_UpdateAccountSettings_IPv6EnabledGroups(t *testing.T) {
|
func TestDefaultAccountManager_UpdateAccountSettings_IPv6EnabledGroups(t *testing.T) {
|
||||||
manager, _, account, peer1, peer2, peer3 := setupNetworkMapTest(t)
|
manager, _, account, peer1, peer2, peer3 := setupNetworkMapTest(t)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
@@ -892,12 +892,7 @@ func (a *Account) GetPeerConnectionResources(ctx context.Context, peer *nbpeer.P
|
|||||||
generateResources(rule, sourcePeers, FirewallRuleDirectionIN)
|
generateResources(rule, sourcePeers, FirewallRuleDirectionIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth is collected when this peer serves the rule. For bidirectional
|
if peerInDestinations && rule.Protocol == PolicyRuleProtocolNetbirdSSH {
|
||||||
// rules the peer-in-sources side also serves inbound traffic, so it
|
|
||||||
// must be treated as a destination too.
|
|
||||||
peerServesAuth := peerInDestinations || (rule.Bidirectional && peerInSources)
|
|
||||||
|
|
||||||
if peerServesAuth && rule.Protocol == PolicyRuleProtocolNetbirdSSH {
|
|
||||||
sshEnabled = true
|
sshEnabled = true
|
||||||
switch {
|
switch {
|
||||||
case len(rule.AuthorizedGroups) > 0:
|
case len(rule.AuthorizedGroups) > 0:
|
||||||
@@ -929,7 +924,7 @@ func (a *Account) GetPeerConnectionResources(ctx context.Context, peer *nbpeer.P
|
|||||||
default:
|
default:
|
||||||
authorizedUsers[auth.Wildcard] = a.getAllowedUserIDs()
|
authorizedUsers[auth.Wildcard] = a.getAllowedUserIDs()
|
||||||
}
|
}
|
||||||
} else if peerServesAuth && policyRuleImpliesLegacySSH(rule) && peer.SSHEnabled {
|
} else if peerInDestinations && policyRuleImpliesLegacySSH(rule) && peer.SSHEnabled {
|
||||||
sshEnabled = true
|
sshEnabled = true
|
||||||
authorizedUsers[auth.Wildcard] = a.getAllowedUserIDs()
|
authorizedUsers[auth.Wildcard] = a.getAllowedUserIDs()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,12 +341,7 @@ func (a *Account) getPeersGroupsPoliciesRoutes(
|
|||||||
for _, srcGroupID := range rule.Sources {
|
for _, srcGroupID := range rule.Sources {
|
||||||
relevantGroupIDs[srcGroupID] = a.GetGroup(srcGroupID)
|
relevantGroupIDs[srcGroupID] = a.GetGroup(srcGroupID)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// SSH auth requirements are gathered whenever this peer serves
|
|
||||||
// the rule. For bidirectional rules the peer-in-sources side
|
|
||||||
// also serves inbound traffic and must be treated as a destination.
|
|
||||||
if peerInDestinations || (rule.Bidirectional && peerInSources) {
|
|
||||||
if rule.Protocol == PolicyRuleProtocolNetbirdSSH {
|
if rule.Protocol == PolicyRuleProtocolNetbirdSSH {
|
||||||
switch {
|
switch {
|
||||||
case len(rule.AuthorizedGroups) > 0:
|
case len(rule.AuthorizedGroups) > 0:
|
||||||
|
|||||||
@@ -221,12 +221,7 @@ func (c *NetworkMapComponents) getPeerConnectionResources(targetPeerID string) (
|
|||||||
generateResources(rule, sourcePeers, FirewallRuleDirectionIN)
|
generateResources(rule, sourcePeers, FirewallRuleDirectionIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth is collected when this peer serves the rule. For bidirectional
|
if peerInDestinations && rule.Protocol == PolicyRuleProtocolNetbirdSSH {
|
||||||
// rules the peer-in-sources side also serves inbound traffic, so it
|
|
||||||
// must be treated as a destination too.
|
|
||||||
peerServesAuth := peerInDestinations || (rule.Bidirectional && peerInSources)
|
|
||||||
|
|
||||||
if peerServesAuth && rule.Protocol == PolicyRuleProtocolNetbirdSSH {
|
|
||||||
sshEnabled = true
|
sshEnabled = true
|
||||||
switch {
|
switch {
|
||||||
case len(rule.AuthorizedGroups) > 0:
|
case len(rule.AuthorizedGroups) > 0:
|
||||||
@@ -257,7 +252,7 @@ func (c *NetworkMapComponents) getPeerConnectionResources(targetPeerID string) (
|
|||||||
default:
|
default:
|
||||||
authorizedUsers[auth.Wildcard] = c.getAllowedUserIDs()
|
authorizedUsers[auth.Wildcard] = c.getAllowedUserIDs()
|
||||||
}
|
}
|
||||||
} else if peerServesAuth && policyRuleImpliesLegacySSH(rule) && targetPeer.SSHEnabled {
|
} else if peerInDestinations && policyRuleImpliesLegacySSH(rule) && targetPeer.SSHEnabled {
|
||||||
sshEnabled = true
|
sshEnabled = true
|
||||||
authorizedUsers[auth.Wildcard] = c.getAllowedUserIDs()
|
authorizedUsers[auth.Wildcard] = c.getAllowedUserIDs()
|
||||||
}
|
}
|
||||||
@@ -562,6 +557,7 @@ func (c *NetworkMapComponents) getRoutingPeerRoutes(peerID string) (enabledRoute
|
|||||||
return enabledRoutes, disabledRoutes
|
return enabledRoutes, disabledRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (c *NetworkMapComponents) filterRoutesByGroups(routes []*route.Route, groupListMap LookupMap) []*route.Route {
|
func (c *NetworkMapComponents) filterRoutesByGroups(routes []*route.Route, groupListMap LookupMap) []*route.Route {
|
||||||
var filteredRoutes []*route.Route
|
var filteredRoutes []*route.Route
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
|
|||||||
@@ -980,44 +980,6 @@ func TestComponents_SSHAuthorizedUsersContent(t *testing.T) {
|
|||||||
assert.True(t, hasRoot || hasAdmin, "AuthorizedUsers should contain 'root' or 'admin' machine user mapping")
|
assert.True(t, hasRoot || hasAdmin, "AuthorizedUsers should contain 'root' or 'admin' machine user mapping")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestComponents_SSHAuthorizedUsersBidirectionalSource verifies that a peer
|
|
||||||
// on the sources side of a bidirectional NetbirdSSH rule receives the rule's
|
|
||||||
// authorized users. The reverse direction (destinations -> sources) makes
|
|
||||||
// the source-side peer a destination too, so it must be able to authorize
|
|
||||||
// inbound SSH from the rule's destinations.
|
|
||||||
func TestComponents_SSHAuthorizedUsersBidirectionalSource(t *testing.T) {
|
|
||||||
account, validatedPeers := scalableTestAccountWithoutDefaultPolicy(20, 2)
|
|
||||||
|
|
||||||
account.Users["user-dev"] = &types.User{Id: "user-dev", Role: types.UserRoleUser, AccountID: "test-account", AutoGroups: []string{"ssh-users"}}
|
|
||||||
account.Groups["ssh-users"] = &types.Group{ID: "ssh-users", Name: "SSH Users", Peers: []string{}}
|
|
||||||
|
|
||||||
account.Policies = append(account.Policies, &types.Policy{
|
|
||||||
ID: "policy-ssh-bidir", Name: "Bidirectional SSH", Enabled: true, AccountID: "test-account",
|
|
||||||
Rules: []*types.PolicyRule{{
|
|
||||||
ID: "rule-ssh-bidir", Name: "SSH both ways", Enabled: true,
|
|
||||||
Action: types.PolicyTrafficActionAccept, Protocol: types.PolicyRuleProtocolNetbirdSSH,
|
|
||||||
Bidirectional: true,
|
|
||||||
Sources: []string{"group-0"}, Destinations: []string{"group-1"},
|
|
||||||
AuthorizedGroups: map[string][]string{"ssh-users": {"root"}},
|
|
||||||
}},
|
|
||||||
})
|
|
||||||
|
|
||||||
nmSrc := componentsNetworkMap(account, "peer-0", validatedPeers)
|
|
||||||
require.NotNil(t, nmSrc)
|
|
||||||
assert.True(t, nmSrc.EnableSSH, "source-side peer of bidirectional SSH rule should have SSH enabled")
|
|
||||||
require.NotEmpty(t, nmSrc.AuthorizedUsers, "source-side peer should receive authorized users from bidirectional rule")
|
|
||||||
rootUsers, hasRoot := nmSrc.AuthorizedUsers["root"]
|
|
||||||
require.True(t, hasRoot, "source-side peer should map the 'root' local user")
|
|
||||||
_, hasDev := rootUsers["user-dev"]
|
|
||||||
assert.True(t, hasDev, "source-side peer should include 'user-dev' under 'root'")
|
|
||||||
|
|
||||||
nmDst := componentsNetworkMap(account, "peer-10", validatedPeers)
|
|
||||||
require.NotNil(t, nmDst)
|
|
||||||
assert.True(t, nmDst.EnableSSH, "destination-side peer should also have SSH enabled")
|
|
||||||
_, hasRoot = nmDst.AuthorizedUsers["root"]
|
|
||||||
assert.True(t, hasRoot, "destination-side peer should also map the 'root' local user")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestComponents_SSHLegacyImpliedSSH verifies that a non-SSH ALL protocol policy with
|
// TestComponents_SSHLegacyImpliedSSH verifies that a non-SSH ALL protocol policy with
|
||||||
// SSHEnabled peer implies legacy SSH access.
|
// SSHEnabled peer implies legacy SSH access.
|
||||||
func TestComponents_SSHLegacyImpliedSSH(t *testing.T) {
|
func TestComponents_SSHLegacyImpliedSSH(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user