mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-17 14:19:54 +00:00
[management] Allocate and preserve IPv6 overlay addresses for embedded proxy peers (#6132)
This commit is contained in:
@@ -2487,6 +2487,18 @@ func (am *DefaultAccountManager) buildIPv6AllowedPeers(ctx context.Context, tran
|
|||||||
allowedPeers[peerID] = struct{}{}
|
allowedPeers[peerID] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Embedded proxy peers sit outside regular group membership but must
|
||||||
|
// participate in any v6-enabled overlay to reach v6-only peers.
|
||||||
|
peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthNone, accountID, "", "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get peers: %w", err)
|
||||||
|
}
|
||||||
|
for _, p := range peers {
|
||||||
|
if p.ProxyMeta.Embedded {
|
||||||
|
allowedPeers[p.ID] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
return allowedPeers, nil
|
return allowedPeers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -762,16 +762,19 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, accountID, setupKe
|
|||||||
newPeer.IP = freeIP
|
newPeer.IP = freeIP
|
||||||
|
|
||||||
if len(settings.IPv6EnabledGroups) > 0 && network.NetV6.IP != nil {
|
if len(settings.IPv6EnabledGroups) > 0 && network.NetV6.IP != nil {
|
||||||
var allGroupID string
|
// Embedded proxy peers are not group members but participate in any
|
||||||
if !peer.ProxyMeta.Embedded {
|
// IPv6-enabled overlay so reverse-proxy traffic reaches v6-only peers.
|
||||||
allGroup, err := am.Store.GetGroupByName(ctx, store.LockingStrengthNone, accountID, "All")
|
allocate := peer.ProxyMeta.Embedded
|
||||||
if err != nil {
|
if !allocate {
|
||||||
log.WithContext(ctx).Debugf("get All group for IPv6 allocation: %v", err)
|
var allGroupID string
|
||||||
} else {
|
if allGroup, err := am.Store.GetGroupByName(ctx, store.LockingStrengthNone, accountID, types.GroupAllName); err == nil {
|
||||||
allGroupID = allGroup.ID
|
allGroupID = allGroup.ID
|
||||||
|
} else {
|
||||||
|
log.WithContext(ctx).Debugf("get All group for IPv6 allocation: %v", err)
|
||||||
}
|
}
|
||||||
|
allocate = peerWillHaveIPv6(settings, peerAddConfig.GroupsToAdd, allGroupID)
|
||||||
}
|
}
|
||||||
if peerWillHaveIPv6(settings, peerAddConfig.GroupsToAdd, allGroupID) {
|
if allocate {
|
||||||
v6Prefix, err := netip.ParsePrefix(network.NetV6.String())
|
v6Prefix, err := netip.ParsePrefix(network.NetV6.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("parse IPv6 prefix: %w", err)
|
return nil, nil, nil, fmt.Errorf("parse IPv6 prefix: %w", err)
|
||||||
|
|||||||
@@ -598,28 +598,21 @@ func (a *Account) GetPeerGroups(peerID string) LookupMap {
|
|||||||
return groupList
|
return groupList
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerIPv6Allowed reports whether the given peer is in any of the account's IPv6 enabled groups.
|
// PeerIPv6Allowed reports whether the given peer participates in the IPv6 overlay.
|
||||||
// Returns false if IPv6 is disabled or no groups are configured.
|
// Returns false if IPv6 is disabled or no groups are configured.
|
||||||
func (a *Account) PeerIPv6Allowed(peerID string) bool {
|
func (a *Account) PeerIPv6Allowed(peerID string) bool {
|
||||||
if len(a.Settings.IPv6EnabledGroups) == 0 {
|
_, ok := a.peerIPv6AllowedSet()[peerID]
|
||||||
return false
|
return ok
|
||||||
}
|
|
||||||
|
|
||||||
for _, groupID := range a.Settings.IPv6EnabledGroups {
|
|
||||||
group, ok := a.Groups[groupID]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if slices.Contains(group.Peers, peerID) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// peerIPv6AllowedSet returns a set of peer IDs that belong to any IPv6-enabled group.
|
// peerIPv6AllowedSet returns the set of peer IDs that participate in the IPv6 overlay:
|
||||||
|
// members of any IPv6-enabled group, plus every embedded proxy peer (which sit outside
|
||||||
|
// regular group membership but must reach v6-enabled peers).
|
||||||
func (a *Account) peerIPv6AllowedSet() map[string]struct{} {
|
func (a *Account) peerIPv6AllowedSet() map[string]struct{} {
|
||||||
result := make(map[string]struct{})
|
result := make(map[string]struct{})
|
||||||
|
if len(a.Settings.IPv6EnabledGroups) == 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
for _, groupID := range a.Settings.IPv6EnabledGroups {
|
for _, groupID := range a.Settings.IPv6EnabledGroups {
|
||||||
group, ok := a.Groups[groupID]
|
group, ok := a.Groups[groupID]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -629,6 +622,11 @@ func (a *Account) peerIPv6AllowedSet() map[string]struct{} {
|
|||||||
result[peerID] = struct{}{}
|
result[peerID] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for id, p := range a.Peers {
|
||||||
|
if p != nil && p.ProxyMeta.Embedded {
|
||||||
|
result[id] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,9 +92,12 @@ func (g *Group) HasPeers() bool {
|
|||||||
return len(g.Peers) > 0
|
return len(g.Peers) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GroupAllName is the reserved name of the default group that contains every peer in an account.
|
||||||
|
const GroupAllName = "All"
|
||||||
|
|
||||||
// IsGroupAll checks if the group is a default "All" group.
|
// IsGroupAll checks if the group is a default "All" group.
|
||||||
func (g *Group) IsGroupAll() bool {
|
func (g *Group) IsGroupAll() bool {
|
||||||
return g.Name == "All"
|
return g.Name == GroupAllName
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPeer adds peerID to Peers if not present, returning true if added.
|
// AddPeer adds peerID to Peers if not present, returning true if added.
|
||||||
|
|||||||
@@ -232,3 +232,33 @@ func TestIPv6RecalculationOnGroupChange(t *testing.T) {
|
|||||||
assert.True(t, account.PeerIPv6Allowed("peer3"), "peer3 now in infra")
|
assert.True(t, account.PeerIPv6Allowed("peer3"), "peer3 now in infra")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPeerIPv6AllowedEmbeddedProxy(t *testing.T) {
|
||||||
|
account := &Account{
|
||||||
|
Peers: map[string]*nbpeer.Peer{
|
||||||
|
"peer1": {ID: "peer1"},
|
||||||
|
"proxy": {ID: "proxy", ProxyMeta: nbpeer.ProxyMeta{Embedded: true, Cluster: "netbird.test"}},
|
||||||
|
},
|
||||||
|
Groups: map[string]*Group{
|
||||||
|
"group-devs": {ID: "group-devs", Peers: []string{"peer1"}},
|
||||||
|
},
|
||||||
|
Settings: &Settings{},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("embedded proxy allowed when any v6 group exists, without group membership", func(t *testing.T) {
|
||||||
|
account.Settings.IPv6EnabledGroups = []string{"group-devs"}
|
||||||
|
assert.True(t, account.PeerIPv6Allowed("proxy"), "embedded proxy participates in v6 overlay")
|
||||||
|
assert.True(t, account.PeerIPv6Allowed("peer1"), "regular peer in enabled group still allowed")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("embedded proxy denied when no v6 group enabled", func(t *testing.T) {
|
||||||
|
account.Settings.IPv6EnabledGroups = nil
|
||||||
|
assert.False(t, account.PeerIPv6Allowed("proxy"), "v6 disabled account-wide denies embedded proxies too")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("non-embedded peer outside any enabled group is not pulled in", func(t *testing.T) {
|
||||||
|
account.Settings.IPv6EnabledGroups = []string{"group-devs"}
|
||||||
|
account.Peers["lonely"] = &nbpeer.Peer{ID: "lonely"}
|
||||||
|
assert.False(t, account.PeerIPv6Allowed("lonely"), "embedded-proxy bypass must not leak to regular peers")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user