mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-13 17:09:07 +02:00
[management] setting to return error on name collision during AddPeer (#8154)
This commit is contained in:
@@ -3050,3 +3050,214 @@ func TestResolvePeerLocation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPeer_HostnameCollisionRejected(t *testing.T) {
|
||||
manager, _, err := createManager(t)
|
||||
require.NoError(t, err)
|
||||
|
||||
account := newAccountWithId(context.Background(), "test-account", "owner", "", "", "", false)
|
||||
account.Settings.PeerHostnameCollisionRejected = true
|
||||
require.NoError(t, manager.Store.SaveAccount(context.Background(), account))
|
||||
|
||||
setupKey, err := manager.CreateSetupKey(context.Background(), account.Id, "key", types.SetupKeyReusable, time.Hour, nil, 100, "owner", false, false)
|
||||
require.NoError(t, err)
|
||||
ephemeralKey, err := manager.CreateSetupKey(context.Background(), account.Id, "ephemeral", types.SetupKeyReusable, time.Hour, nil, 100, "owner", true, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
addPeer := func(hostname, key string) (*nbpeer.Peer, error) {
|
||||
wgKey, err := wgtypes.GenerateKey()
|
||||
require.NoError(t, err)
|
||||
peer := &nbpeer.Peer{
|
||||
Key: wgKey.PublicKey().String(),
|
||||
Meta: nbpeer.PeerSystemMeta{Hostname: hostname, OS: "linux"},
|
||||
}
|
||||
added, _, _, _, err := manager.AddPeer(context.Background(), "", key, "", peer, false)
|
||||
return added, err
|
||||
}
|
||||
|
||||
first, err := addPeer("my-host", setupKey.Key)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "my-host", first.DNSLabel)
|
||||
|
||||
_, err = addPeer("My-Host", setupKey.Key)
|
||||
require.Error(t, err)
|
||||
e, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, status.PermissionDenied, e.Type())
|
||||
|
||||
other, err := addPeer("other-host", setupKey.Key)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "other-host", other.DNSLabel)
|
||||
|
||||
_, err = addPeer("my-host", ephemeralKey.Key)
|
||||
require.Error(t, err)
|
||||
e, ok = status.FromError(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, status.PermissionDenied, e.Type())
|
||||
|
||||
ephemeral, err := addPeer("ephemeral-host", ephemeralKey.Key)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "ephemeral-host", ephemeral.DNSLabel)
|
||||
|
||||
account.Settings.PeerHostnameCollisionRejected = false
|
||||
require.NoError(t, manager.Store.SaveAccountSettings(context.Background(), account.Id, account.Settings))
|
||||
|
||||
suffixed, err := addPeer("my-host", setupKey.Key)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, strings.HasPrefix(suffixed.DNSLabel, "my-host-"))
|
||||
}
|
||||
|
||||
func TestUpdatePeer_HostnameCollisionRejected(t *testing.T) {
|
||||
manager, _, err := createManager(t)
|
||||
require.NoError(t, err)
|
||||
|
||||
account := newAccountWithId(context.Background(), "test-account", "owner", "", "", "", false)
|
||||
account.Settings.PeerHostnameCollisionRejected = true
|
||||
require.NoError(t, manager.Store.SaveAccount(context.Background(), account))
|
||||
|
||||
addPeer := func(hostname string) *nbpeer.Peer {
|
||||
wgKey, err := wgtypes.GenerateKey()
|
||||
require.NoError(t, err)
|
||||
added, _, _, _, err := manager.AddPeer(context.Background(), "", "", "owner", &nbpeer.Peer{
|
||||
Key: wgKey.PublicKey().String(),
|
||||
Meta: nbpeer.PeerSystemMeta{Hostname: hostname, OS: "linux"},
|
||||
}, false)
|
||||
require.NoError(t, err)
|
||||
return added
|
||||
}
|
||||
first := addPeer("web-server")
|
||||
second := addPeer("old-name")
|
||||
|
||||
update := second.Copy()
|
||||
update.Name = "Web-Server"
|
||||
_, err = manager.UpdatePeer(context.Background(), account.Id, "owner", update)
|
||||
require.Error(t, err)
|
||||
e, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, status.PermissionDenied, e.Type())
|
||||
|
||||
update = second.Copy()
|
||||
update.Name = "api-server.example.com"
|
||||
updated, err := manager.UpdatePeer(context.Background(), account.Id, "owner", update)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "api-server", updated.DNSLabel)
|
||||
|
||||
update = first.Copy()
|
||||
update.Name = "WEB-SERVER"
|
||||
updated, err = manager.UpdatePeer(context.Background(), account.Id, "owner", update)
|
||||
require.NoError(t, err, "renaming to a different spelling of the own label must not collide with itself")
|
||||
assert.Equal(t, "web-server", updated.DNSLabel)
|
||||
}
|
||||
|
||||
func TestAddPeer_HostnameCollisionRejectedConcurrent(t *testing.T) {
|
||||
manager, _, err := createManager(t)
|
||||
require.NoError(t, err)
|
||||
|
||||
account := newAccountWithId(context.Background(), "test-account", "owner", "", "", "", false)
|
||||
account.Settings.PeerHostnameCollisionRejected = true
|
||||
require.NoError(t, manager.Store.SaveAccount(context.Background(), account))
|
||||
|
||||
setupKey, err := manager.CreateSetupKey(context.Background(), account.Id, "key", types.SetupKeyReusable, time.Hour, nil, 100, "owner", false, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
const attempts = 20
|
||||
start := make(chan struct{})
|
||||
errs := make(chan error, attempts)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < attempts; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
wgKey, err := wgtypes.GenerateKey()
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
<-start
|
||||
_, _, _, _, err = manager.AddPeer(context.Background(), "", setupKey.Key, "", &nbpeer.Peer{
|
||||
Key: wgKey.PublicKey().String(),
|
||||
Meta: nbpeer.PeerSystemMeta{Hostname: "same-host", OS: "linux"},
|
||||
}, false)
|
||||
errs <- err
|
||||
}()
|
||||
}
|
||||
close(start)
|
||||
wg.Wait()
|
||||
close(errs)
|
||||
|
||||
succeeded := 0
|
||||
for err := range errs {
|
||||
if err == nil {
|
||||
succeeded++
|
||||
continue
|
||||
}
|
||||
e, ok := status.FromError(err)
|
||||
require.True(t, ok, "unexpected error: %v", err)
|
||||
assert.Equal(t, status.PermissionDenied, e.Type(), "unexpected error: %v", err)
|
||||
}
|
||||
assert.Equal(t, 1, succeeded)
|
||||
|
||||
peers, err := manager.Store.GetAccountPeers(context.Background(), store.LockingStrengthNone, account.Id, "", "", "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, peers, 1)
|
||||
assert.Equal(t, "same-host", peers[0].DNSLabel)
|
||||
}
|
||||
|
||||
func TestAddPeer_IPCollisionKeepsLabel(t *testing.T) {
|
||||
for _, rejectCollision := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("collision rejected %t", rejectCollision), func(t *testing.T) {
|
||||
manager, _, err := createManager(t)
|
||||
require.NoError(t, err)
|
||||
|
||||
account := newAccountWithId(context.Background(), "test-account", "owner", "", "", "", false)
|
||||
account.Settings.PeerHostnameCollisionRejected = rejectCollision
|
||||
_, smallNet, err := net.ParseCIDR("100.64.0.0/26")
|
||||
require.NoError(t, err)
|
||||
account.Network.Net = *smallNet
|
||||
require.NoError(t, manager.Store.SaveAccount(context.Background(), account))
|
||||
|
||||
const totalPeers = 20
|
||||
for i := 0; i < totalPeers; i++ {
|
||||
wgKey, err := wgtypes.GenerateKey()
|
||||
require.NoError(t, err)
|
||||
hostname := fmt.Sprintf("host-%d", i)
|
||||
added, _, _, _, err := manager.AddPeer(context.Background(), "", "", "owner", &nbpeer.Peer{
|
||||
Key: wgKey.PublicKey().String(),
|
||||
Meta: nbpeer.PeerSystemMeta{Hostname: hostname, OS: "linux"},
|
||||
}, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, hostname, added.DNSLabel, "an IP collision must not change the DNS label")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPeer_ExhaustedIPAllocationFails(t *testing.T) {
|
||||
manager, _, err := createManager(t)
|
||||
require.NoError(t, err)
|
||||
|
||||
account := newAccountWithId(context.Background(), "test-account", "owner", "", "", "", false)
|
||||
_, tinyNet, err := net.ParseCIDR("100.64.0.0/30")
|
||||
require.NoError(t, err)
|
||||
account.Network.Net = *tinyNet
|
||||
require.NoError(t, manager.Store.SaveAccount(context.Background(), account))
|
||||
|
||||
addPeer := func(hostname string) error {
|
||||
wgKey, err := wgtypes.GenerateKey()
|
||||
require.NoError(t, err)
|
||||
_, _, _, _, err = manager.AddPeer(context.Background(), "", "", "owner", &nbpeer.Peer{
|
||||
Key: wgKey.PublicKey().String(),
|
||||
Meta: nbpeer.PeerSystemMeta{Hostname: hostname, OS: "linux"},
|
||||
}, false)
|
||||
return err
|
||||
}
|
||||
require.NoError(t, addPeer("host-1"))
|
||||
require.NoError(t, addPeer("host-2"))
|
||||
|
||||
err = addPeer("host-3")
|
||||
require.Error(t, err, "no free IP left, registration must fail")
|
||||
|
||||
peers, err := manager.Store.GetAccountPeers(context.Background(), store.LockingStrengthNone, account.Id, "", "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, peers, 2)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user