mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-22 13:29:08 +02:00
Merge remote-tracking branch 'origin/main' into embedded-vnc
# Conflicts: # client/cmd/up.go # client/internal/engine.go # client/internal/profilemanager/config.go # client/proto/daemon.pb.go # client/server/mdm.go # client/server/server.go # client/status/status.go # client/ui/main.go # client/ui/services/windowmanager.go # management/internals/shared/grpc/conversion.go # management/server/store/sql_store.go # management/server/store/sql_store_test.go # shared/management/networkmap/envelope.go # shared/management/proto/management.pb.go
This commit is contained in:
@@ -198,6 +198,7 @@ func twinPeer(p *nbpeer.Peer) *nmdata.Peer {
|
||||
KernelVersion: p.Meta.KernelVersion,
|
||||
NetworkAddresses: networkAddresses,
|
||||
Files: files,
|
||||
Certificates: p.Meta.Certificates,
|
||||
Capabilities: p.Meta.Capabilities,
|
||||
SyncMessageVersion: p.Meta.SyncMessageVersion,
|
||||
Flags: nmdata.Flags{
|
||||
@@ -213,8 +214,7 @@ func twinPeer(p *nbpeer.Peer) *nmdata.Peer {
|
||||
}
|
||||
}
|
||||
|
||||
// TwinPeer converts a real peer to its slim nmdata twin. Exported for the
|
||||
// port-forwarding integration, which builds proxy NetworkMaps holding twins.
|
||||
// TwinPeer converts a real peer to its slim nmdata twin.
|
||||
func TwinPeer(p *nbpeer.Peer) *nmdata.Peer {
|
||||
return twinPeer(p)
|
||||
}
|
||||
@@ -455,6 +455,9 @@ func TwinPostureChecks(pc *posture.Checks) *nmdata.PostureChecks {
|
||||
}
|
||||
out.Checks.ProcessCheck = &nmdata.ProcessCheck{Processes: procs}
|
||||
}
|
||||
if def.CertificateCheck != nil {
|
||||
out.Checks.CertificateCheck = &nmdata.CertificateCheck{CACertificates: def.CertificateCheck.CACertificates}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
type FirewallRule = sharedtypes.FirewallRule
|
||||
|
||||
type NetworkMap = sharedtypes.NetworkMap
|
||||
type ForwardingRule = sharedtypes.ForwardingRule
|
||||
|
||||
type PolicyTrafficActionType = sharedtypes.PolicyTrafficActionType
|
||||
type PolicyRuleProtocolType = sharedtypes.PolicyRuleProtocolType
|
||||
|
||||
@@ -10,7 +10,6 @@ type (
|
||||
|
||||
DNSSettings = types.DNSSettings
|
||||
FirewallRule = sharedtypes.FirewallRule
|
||||
ForwardingRule = sharedtypes.ForwardingRule
|
||||
Group = types.Group
|
||||
Network = types.Network
|
||||
Policy = types.Policy
|
||||
|
||||
@@ -19,7 +19,6 @@ type NetworkMap struct {
|
||||
OfflinePeers []*ComponentPeer
|
||||
FirewallRules []*FirewallRule
|
||||
RoutesFirewallRules []*RouteFirewallRule
|
||||
ForwardingRules []*ForwardingRule
|
||||
AuthorizedUsers map[string]map[string]struct{}
|
||||
EnableSSH bool
|
||||
// ForceRoutingPeerDNSResolution forces the peer to run/use routing-peer DNS
|
||||
|
||||
@@ -320,7 +320,6 @@ func canonicalize(nm *proto.NetworkMap) {
|
||||
slices.SortFunc(nm.Routes, cmpRoute)
|
||||
slices.SortFunc(nm.FirewallRules, cmpFirewallRule)
|
||||
slices.SortFunc(nm.RoutesFirewallRules, cmpRouteFirewallRule)
|
||||
slices.SortFunc(nm.ForwardingRules, cmpForwardingRule)
|
||||
|
||||
for _, r := range nm.FirewallRules {
|
||||
slices.SortFunc(r.SourcePrefixes, bytes.Compare)
|
||||
@@ -550,16 +549,6 @@ func cmpRouteFirewallRule(a, b *proto.RouteFirewallRule) int {
|
||||
return boolCmp(a.IsDynamic, b.IsDynamic)
|
||||
}
|
||||
|
||||
func cmpForwardingRule(a, b *proto.ForwardingRule) int {
|
||||
if a == nil || b == nil {
|
||||
return boolCmp(a == nil, b == nil)
|
||||
}
|
||||
if c := cmp.Compare(int32(a.Protocol), int32(b.Protocol)); c != 0 {
|
||||
return c
|
||||
}
|
||||
return bytes.Compare(a.TranslatedAddress, b.TranslatedAddress)
|
||||
}
|
||||
|
||||
func portInfoKey(pi *proto.PortInfo) string {
|
||||
if pi == nil {
|
||||
return ""
|
||||
@@ -591,7 +580,6 @@ func describeDivergence(legacy, updated *proto.NetworkMap, accountID, peerID str
|
||||
{"Routes", len(legacy.Routes), len(updated.Routes), func() string { return diffLists(legacy.Routes, updated.Routes) }},
|
||||
{"FirewallRules", len(legacy.FirewallRules), len(updated.FirewallRules), func() string { return diffLists(legacy.FirewallRules, updated.FirewallRules) }},
|
||||
{"RoutesFirewallRules", len(legacy.RoutesFirewallRules), len(updated.RoutesFirewallRules), func() string { return diffLists(legacy.RoutesFirewallRules, updated.RoutesFirewallRules) }},
|
||||
{"ForwardingRules", len(legacy.ForwardingRules), len(updated.ForwardingRules), func() string { return diffLists(legacy.ForwardingRules, updated.ForwardingRules) }},
|
||||
}
|
||||
for _, l := range lens {
|
||||
if l.a != l.b {
|
||||
|
||||
@@ -186,14 +186,6 @@ func ToProtoNetworkMap(
|
||||
pm.RoutesFirewallRules = routesFirewallRules
|
||||
pm.RoutesFirewallRulesIsEmpty = len(routesFirewallRules) == 0
|
||||
|
||||
if nm.ForwardingRules != nil {
|
||||
forwardingRules := make([]*proto.ForwardingRule, 0, len(nm.ForwardingRules))
|
||||
for _, rule := range nm.ForwardingRules {
|
||||
forwardingRules = append(forwardingRules, rule.ToProto())
|
||||
}
|
||||
pm.ForwardingRules = forwardingRules
|
||||
}
|
||||
|
||||
if nm.AuthorizedUsers != nil {
|
||||
hashedUsers, machineUsers := networkmap.BuildAuthorizedUsersProto(ctx, nm.AuthorizedUsers)
|
||||
userIDClaim := auth.DefaultUserIDClaim
|
||||
|
||||
@@ -118,14 +118,9 @@ func AllocatePeerIP(prefix netip.Prefix, takenIps []netip.Addr) (netip.Addr, err
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
|
||||
b := prefix.Masked().Addr().As4()
|
||||
baseIP := binary.BigEndian.Uint32(b[:])
|
||||
hostBits := 32 - prefix.Bits()
|
||||
totalIPs := uint32(1 << hostBits)
|
||||
baseIP, firstOffset, lastOffset := ipv4PeerRange(prefix)
|
||||
|
||||
taken := make(map[uint32]struct{}, len(takenIps)+1)
|
||||
taken[baseIP] = struct{}{} // reserve network IP
|
||||
taken[baseIP+totalIPs-1] = struct{}{} // reserve broadcast IP
|
||||
taken := make(map[uint32]struct{}, len(takenIps))
|
||||
|
||||
for _, ip := range takenIps {
|
||||
if !ip.Is4() {
|
||||
@@ -135,17 +130,16 @@ func AllocatePeerIP(prefix netip.Prefix, takenIps []netip.Addr) (netip.Addr, err
|
||||
taken[binary.BigEndian.Uint32(ab[:])] = struct{}{}
|
||||
}
|
||||
|
||||
maxAttempts := (int(totalIPs) - len(taken)) / 100
|
||||
maxAttempts := (int(lastOffset-firstOffset+1) - len(taken)) / 100
|
||||
|
||||
for i := 0; i < maxAttempts; i++ {
|
||||
offset := uint32(util.RandIntn(int(totalIPs-2))) + 1
|
||||
candidate := baseIP + offset
|
||||
candidate := baseIP + randomOffset(firstOffset, lastOffset)
|
||||
if _, exists := taken[candidate]; !exists {
|
||||
return uint32ToIP(candidate), nil
|
||||
}
|
||||
}
|
||||
|
||||
for offset := uint32(1); offset < totalIPs-1; offset++ {
|
||||
for offset := firstOffset; offset <= lastOffset; offset++ {
|
||||
candidate := baseIP + offset
|
||||
if _, exists := taken[candidate]; !exists {
|
||||
return uint32ToIP(candidate), nil
|
||||
@@ -161,15 +155,38 @@ func AllocateRandomPeerIP(prefix netip.Prefix) (netip.Addr, error) {
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
|
||||
baseIP, firstOffset, lastOffset := ipv4PeerRange(prefix)
|
||||
return uint32ToIP(baseIP + randomOffset(firstOffset, lastOffset)), nil
|
||||
}
|
||||
|
||||
// IsReservedPeerIP reports whether ip is an address of the IPv4 prefix that is never assigned to a peer:
|
||||
// the network address, the broadcast address or the address before it, which clients use for their DNS resolver.
|
||||
// It returns false if prefix is not a valid IPv4 peer network or ip is not within it.
|
||||
func IsReservedPeerIP(prefix netip.Prefix, ip netip.Addr) bool {
|
||||
ip = ip.Unmap()
|
||||
if validateIPv4Prefix(prefix) != nil || !ip.Is4() || !prefix.Contains(ip) {
|
||||
return false
|
||||
}
|
||||
|
||||
baseIP, firstOffset, lastOffset := ipv4PeerRange(prefix)
|
||||
b := ip.As4()
|
||||
offset := binary.BigEndian.Uint32(b[:]) - baseIP
|
||||
return offset < firstOffset || offset > lastOffset
|
||||
}
|
||||
|
||||
// ipv4PeerRange returns the network address of the IPv4 prefix as uint32 and the first and last offset
|
||||
// from it that can be assigned to a peer. Reserved are the network address, the broadcast address and the
|
||||
// address before it, which clients in userspace and netstack mode use for their DNS resolver.
|
||||
func ipv4PeerRange(prefix netip.Prefix) (baseIP, firstOffset, lastOffset uint32) {
|
||||
b := prefix.Masked().Addr().As4()
|
||||
baseIP := binary.BigEndian.Uint32(b[:])
|
||||
hostBits := 32 - prefix.Bits()
|
||||
totalIPs := uint32(1 << hostBits)
|
||||
broadcastOffset := uint32(1)<<(32-prefix.Bits()) - 1
|
||||
dnsResolverOffset := broadcastOffset - 1
|
||||
return binary.BigEndian.Uint32(b[:]), 1, dnsResolverOffset - 1
|
||||
}
|
||||
|
||||
offset := uint32(util.RandIntn(int(totalIPs-2))) + 1
|
||||
|
||||
candidate := baseIP + offset
|
||||
return uint32ToIP(candidate), nil
|
||||
// randomOffset returns a random offset in [firstOffset, lastOffset].
|
||||
func randomOffset(firstOffset, lastOffset uint32) uint32 {
|
||||
return firstOffset + uint32(util.RandIntn(int(lastOffset-firstOffset+1)))
|
||||
}
|
||||
|
||||
// AllocateRandomPeerIPv6 picks a random host address within the given IPv6 prefix.
|
||||
|
||||
@@ -42,12 +42,12 @@ func TestAllocatePeerIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAllocatePeerIPSmallSubnet(t *testing.T) {
|
||||
// Test /27 network (10.0.0.0/27) - should only have 30 usable IPs (10.0.0.1 to 10.0.0.30)
|
||||
// Test /27 network (10.0.0.0/27) - should only have 29 usable IPs (10.0.0.1 to 10.0.0.29)
|
||||
prefix := netip.MustParsePrefix("10.0.0.0/27")
|
||||
var ips []netip.Addr
|
||||
|
||||
// Allocate all available IPs in the /27 network
|
||||
for i := 0; i < 30; i++ {
|
||||
for i := 0; i < 29; i++ {
|
||||
ip, err := AllocatePeerIP(prefix, ips)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -61,7 +61,7 @@ func TestAllocatePeerIPSmallSubnet(t *testing.T) {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
|
||||
assert.Len(t, ips, 30)
|
||||
assert.Len(t, ips, 29)
|
||||
|
||||
// Verify all IPs are unique
|
||||
uniq := make(map[string]struct{})
|
||||
@@ -86,13 +86,13 @@ func TestAllocatePeerIPVariousCIDRs(t *testing.T) {
|
||||
cidr string
|
||||
expectedUsable int
|
||||
}{
|
||||
{"/30 network", "192.168.1.0/30", 2}, // 4 total - 2 reserved = 2 usable
|
||||
{"/29 network", "192.168.1.0/29", 6}, // 8 total - 2 reserved = 6 usable
|
||||
{"/28 network", "192.168.1.0/28", 14}, // 16 total - 2 reserved = 14 usable
|
||||
{"/27 network", "192.168.1.0/27", 30}, // 32 total - 2 reserved = 30 usable
|
||||
{"/26 network", "192.168.1.0/26", 62}, // 64 total - 2 reserved = 62 usable
|
||||
{"/25 network", "192.168.1.0/25", 126}, // 128 total - 2 reserved = 126 usable
|
||||
{"/16 network", "10.0.0.0/16", 65534}, // 65536 total - 2 reserved = 65534 usable
|
||||
{"/30 network", "192.168.1.0/30", 1}, // 4 total - 3 reserved = 1 usable
|
||||
{"/29 network", "192.168.1.0/29", 5}, // 8 total - 3 reserved = 5 usable
|
||||
{"/28 network", "192.168.1.0/28", 13}, // 16 total - 3 reserved = 13 usable
|
||||
{"/27 network", "192.168.1.0/27", 29}, // 32 total - 3 reserved = 29 usable
|
||||
{"/26 network", "192.168.1.0/26", 61}, // 64 total - 3 reserved = 61 usable
|
||||
{"/25 network", "192.168.1.0/25", 125}, // 128 total - 3 reserved = 125 usable
|
||||
{"/16 network", "10.0.0.0/16", 65533}, // 65536 total - 3 reserved = 65533 usable
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -117,14 +117,16 @@ func TestAllocatePeerIPVariousCIDRs(t *testing.T) {
|
||||
// Verify IP is within the correct range
|
||||
assert.True(t, prefix.Contains(ip), "allocated IP %s is not within network %s", ip.String(), prefix.String())
|
||||
|
||||
// Verify IP is not network or broadcast address
|
||||
// Verify IP is not network, DNS resolver or broadcast address
|
||||
networkAddr := prefix.Masked().Addr()
|
||||
hostBits := 32 - prefix.Bits()
|
||||
b := networkAddr.As4()
|
||||
baseIP := binary.BigEndian.Uint32(b[:])
|
||||
dnsIP := uint32ToIP(baseIP + (1 << hostBits) - 2)
|
||||
broadcastIP := uint32ToIP(baseIP + (1 << hostBits) - 1)
|
||||
|
||||
assert.NotEqual(t, networkAddr, ip, "allocated network address %s", ip.String())
|
||||
assert.NotEqual(t, dnsIP, ip, "allocated DNS resolver address %s", ip.String())
|
||||
assert.NotEqual(t, broadcastIP, ip, "allocated broadcast address %s", ip.String())
|
||||
|
||||
ips = append(ips, ip)
|
||||
@@ -143,6 +145,68 @@ func TestAllocatePeerIPVariousCIDRs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocatePeerIPSkipsDNSResolverIP(t *testing.T) {
|
||||
prefix := netip.MustParsePrefix("192.168.1.0/29")
|
||||
ips := []netip.Addr{
|
||||
netip.MustParseAddr("192.168.1.1"),
|
||||
netip.MustParseAddr("192.168.1.2"),
|
||||
netip.MustParseAddr("192.168.1.3"),
|
||||
netip.MustParseAddr("192.168.1.4"),
|
||||
}
|
||||
|
||||
ip, err := AllocatePeerIP(prefix, ips)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, netip.MustParseAddr("192.168.1.5"), ip)
|
||||
|
||||
_, err = AllocatePeerIP(prefix, append(ips, ip))
|
||||
assert.Error(t, err, "192.168.1.6 is the DNS resolver address and must not be allocated")
|
||||
}
|
||||
|
||||
func TestAllocateRandomPeerIPSkipsReservedIPs(t *testing.T) {
|
||||
for _, cidr := range []string{"192.168.1.0/30", "192.168.1.0/29"} {
|
||||
t.Run(cidr, func(t *testing.T) {
|
||||
prefix := netip.MustParsePrefix(cidr)
|
||||
for i := 0; i < 1000; i++ {
|
||||
ip, err := AllocateRandomPeerIP(prefix)
|
||||
require.NoError(t, err)
|
||||
require.True(t, prefix.Contains(ip), "allocated IP %s is not within network %s", ip, prefix)
|
||||
require.False(t, IsReservedPeerIP(prefix, ip), "allocated reserved IP %s", ip)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ip, err := AllocateRandomPeerIP(netip.MustParsePrefix("192.168.1.0/30"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, netip.MustParseAddr("192.168.1.1"), ip)
|
||||
}
|
||||
|
||||
func TestIsReservedPeerIP(t *testing.T) {
|
||||
prefix := netip.MustParsePrefix("100.64.0.0/24")
|
||||
testCases := []struct {
|
||||
ip string
|
||||
reserved bool
|
||||
}{
|
||||
{"100.64.0.0", true},
|
||||
{"100.64.0.1", false},
|
||||
{"100.64.0.253", false},
|
||||
{"100.64.0.254", true},
|
||||
{"100.64.0.255", true},
|
||||
{"::ffff:100.64.0.254", true},
|
||||
{"100.64.1.254", false},
|
||||
{"fd12:3456:7890:abcd::1", false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.ip, func(t *testing.T) {
|
||||
assert.Equal(t, tc.reserved, IsReservedPeerIP(prefix, netip.MustParseAddr(tc.ip)))
|
||||
})
|
||||
}
|
||||
|
||||
smallest := netip.MustParsePrefix("192.168.1.0/30")
|
||||
assert.False(t, IsReservedPeerIP(smallest, netip.MustParseAddr("192.168.1.1")))
|
||||
assert.True(t, IsReservedPeerIP(smallest, netip.MustParseAddr("192.168.1.2")))
|
||||
}
|
||||
|
||||
func TestAllocateIPv4InvalidPrefixes(t *testing.T) {
|
||||
prefixes := []netip.Prefix{
|
||||
{},
|
||||
|
||||
@@ -76,6 +76,10 @@ type Settings struct {
|
||||
// MetricsPushEnabled globally enables or disables client metrics push for the account
|
||||
MetricsPushEnabled bool `gorm:"default:false"`
|
||||
|
||||
// PeerHostnameCollisionRejected rejects registering or renaming a peer to a hostname
|
||||
// already used by another peer of the account instead of suffixing it
|
||||
PeerHostnameCollisionRejected bool `gorm:"default:false"`
|
||||
|
||||
// AgentNetworkOnly limits the dashboard to the Agent Network surface for this account.
|
||||
// Set for accounts created via netbird.ai signups; users can disable it later.
|
||||
AgentNetworkOnly bool `gorm:"default:false"`
|
||||
@@ -123,6 +127,7 @@ func (s *Settings) Copy() *Settings {
|
||||
AutoUpdateAlways: s.AutoUpdateAlways,
|
||||
IPv6EnabledGroups: slices.Clone(s.IPv6EnabledGroups),
|
||||
MetricsPushEnabled: s.MetricsPushEnabled,
|
||||
PeerHostnameCollisionRejected: s.PeerHostnameCollisionRejected,
|
||||
AgentNetworkOnly: s.AgentNetworkOnly,
|
||||
EmbeddedIdpEnabled: s.EmbeddedIdpEnabled,
|
||||
LocalAuthDisabled: s.LocalAuthDisabled,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package types
|
||||
|
||||
type Engine string
|
||||
import "github.com/netbirdio/netbird/management/internals/shared/db"
|
||||
|
||||
type Engine = db.Engine
|
||||
|
||||
const (
|
||||
PostgresStoreEngine Engine = "postgres"
|
||||
PostgresStoreEngine = db.PostgresStoreEngine
|
||||
FileStoreEngine Engine = "jsonfile"
|
||||
SqliteStoreEngine Engine = "sqlite"
|
||||
MysqlStoreEngine Engine = "mysql"
|
||||
SqliteStoreEngine = db.SqliteStoreEngine
|
||||
MysqlStoreEngine = db.MysqlStoreEngine
|
||||
)
|
||||
|
||||
@@ -34,4 +34,7 @@ const (
|
||||
UpdateOperationCreate UpdateOperation = "create"
|
||||
UpdateOperationUpdate UpdateOperation = "update"
|
||||
UpdateOperationDelete UpdateOperation = "delete"
|
||||
// UpdateOperationRefresh is a periodic push that carries no change of its own, so
|
||||
// it stays out of the counters that track what an administrator actually edited.
|
||||
UpdateOperationRefresh UpdateOperation = "refresh"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user